Tải bản đầy đủ (.pdf) (50 trang)

Tài liệu Oracle Unleashed- P22 pdf

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (172.3 KB, 50 trang )

The methods of the Database object are used to retrieve results, apply SQL transactions, and call stored procedures. In
most cases, the application should declare one Database object globally.
The primary means of retrieving results from an ODBC data source is the Snapshot object. The Snapshot object is
created using the CreateSnapshot method of the Database object. The CreateSnapshot method takes two arguments: a
SQL SELECT statement, and a numeric constant used to control processing of the SQL. Unless the application needs to
be portable to different RDBMSs, this numeric constant should be set to DB_SQLPASSTHROUGH, (64), which sends
the statement directly to the server for processing. This mode allows the developer to use the native syntax of the
RDBMS, and prevents the local Microsoft Access engine from attempting to parse and process the SQL. The following
code fragment provides a simple example of the use of the CreateSnapshot method:
Dim dsContacts As Snapshot
Set dsContacts = dbOracle.CreateSnapshot("SELECT a.last_name, a.
first_name, b.phone_nbr FROM individual a, phone
b WHERE a.ID = b.IndividualID(+) ORDER BY 1,
2",
DB_SQLPASSTHROUGH)
The example assumes that the Database object has already connected to the data source. Note that if
DB_SQLPASSTHROUGH is not specified, a syntax error results because the local Access engine attempts to parse the
SQL and does not recognize the outer join syntax.
After applying the SQL and creating the result set, there are numerous methods that can be applied to position the record
pointer in the cursor. The MoveFirst, MoveLast, MoveNext, and MovePrevious methods are the most commonly used,
and their purposes should be self-explanatory. Visual Basic provides the additional method FindFirst to position the
record pointer at the first record matching specific criteria. For example, assuming that the record pointer is positioned at
the first record, the following line would find the first individual with the last name Smith, based on the result set
returned by the previous example:
dsContacts.FindFirst("last_name = 'Smith'")
The criteria supplied as the argument to FindFirst are syntactically equivalent to a WHERE clause in SQL. The
additional methods FindNext, FindPrevious, and FindLast operate on the same basis.
After positioning the record pointer, an application can assign variables to result set column values using the Fields
property of the Snapshot. The Fields collection is simply a representation of the columns in the result set. They can be
accessed by name or by a zero-based index, starting with the left-most column. Listing C.2 demonstrates methods for
assigning application variables to result set data.


Listing C.2. The Fields collection is used to access result set data.
Dim iRow As Long
iRow = 0
dsContacts.MoveFirst
While Not dsContacts.EOF
ReDim Preserve szLast(iRow) As String
ReDim Preserve szFirst(iRow) As String
ReDim Preserve szPhone(iRow) As String
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
szLast(iRow) = dsContacts.Fields("last_name").Value
szFirst(iRow) = dsContacts.Fields(1).Value
szPhone(iRow) = dsContacts.Fields("phone_nbr").Value
dsContacts.MoveNext
Wend
dsContacts.Close
When assigning values from the Fields collection to variables, the application should always check for null
values, as below:
If (IsNull(dsContacts.Fields("last_name")) = False) Then
szLast(iRow) = dsContacts.Fields("last_name").Value
End If
Assigning a null value to an application variable will produce a run-time error.
As evident from listing C.2, the more readable form of accessing the fields collection is to access them by name. When
there are only a few columns in the result set, this point does not seem significant. However, if there are twenty or thirty
columns in the result, it becomes very difficult to identify a column by index. Also, any changes to the SQL which
created the result set may cause the indexes to reference different columns. While it may save a few keystrokes to use
numeric indexes, in the interest of writing maintainable code, the fields collection should be accessed by column name
whenever possible.
When the columns of the result set are not static at design-time, the Count property of the Fields collection can be used
to determine the number of columns, and the Name property , (which applies to each individual field), can be used to
create column headings.

A single Visual Basic object, such as a Database or Snapshot object, cannot be passed as a parameter to a
function, but an array of objects can. When dealing with single object, it can be declared as a single element array so that
it can be passed to functions and subroutines, and generic methods can be written to operate on these objects.
While Visual Basic also provides Dynaset, Table, and QueryDef objects for retrieving result sets, these objects are not
commonly used to communicate with ODBC data sources. The primary difference between a Snapshot and the Dynaset
and Table objects in ODBC environments is that the Snapshot creates a read-only result set, while the Dynaset and Table
objects' result sets can be edited. However, these objects should not be used for database transactions, for reasons that
will be discussed next.
The Dynaset is similar to the Snapshot, except that its result set is refreshed every time a change is made to one of its
underlying tables. This is true only for local database files. With ODBC connections, the Dynaset object is, in essence,
identical to the Snapshot object, except that the Dynaset can be used for in-place updates, deletes, and insertions.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
While Table objects could be used by applications accessing ODBC data sources, it would be unacceptable to do so in
most situations. Using the OpenTable method is not as readable as a SELECT statement in which the columns are clearly
identified and referenced by name, and when accessing Oracle, the Table object is the equivalent of a Dynaset created
with the SQL SELECT * FROM table_name. In both cases, the column list is expanded, and the statement is sent to the
server using the API function SQLExecDirect().
A QueryDef object is similar to a view that is created and stored by the application. The QueryDef object simply stores
frequently used SQL so that it can be read from the database and executed as needed. QueryDefs cannot be created over
ODBC connections. Views are a better alternative, because they are stored in the database with a bound access plan, and
the SQL does not need to be executed from the client application.
The Visual Basic DataControl provides another option for retrieving a result set through ODBC. The DataControl is used
to bind controls to result set columns. To retrieve a result set using a DataControl, its Connect property must be set to the
full connect string, as previously described for the OpenDatabase method. This property can be set at run-time using a
statement like the one below:
Data1.Connect=szConnect
This example assumes that szConnect was constructed as in Listing C.1. The RecordSource property of the DataControl
is used to retrieve the result set. If this property is set at design-time, results will be retrieved as soon as the Connect
property is assigned and a connection is established. If the Connect property is also assigned at design-time, results will
be fetched when the object is instantiated. The RecordSource property can consist of a table name, view name, or a

SELECT statement. The DB_SQLPASSTHROUGH option is not available to the DataControl, so if a SELECT
statement is used as the RecordSource property, it may not contain an outer join or any other Oracle-specific syntax. In
order to use a DataControl with a complex SELECT statement, a view should be created so the DataControl can simply
use the view as the data source.
Controls can be bound to result set columns through the DataControl. A text box, for example, can be bound to a
DataControl by setting its RecordSource property to the name of the DataControl and setting its DataField property to
the name of the column in the result set that it should contain. When the result set is retrieved, the text box is then
automatically populated with the value of the specified column at the current record position. Numerous third-party
vendors provide custom controls that can be bound to result sets in this manner. The standard controls that can be bound
to a result set through the DataControl are limited to text boxes, checkboxes, labels, images, and picture boxes.
The Recordset property of the DataControl is nearly identical to a Dynaset object. The MoveFirst method and other
positioning methods apply to the Recordset property of the DataControl, as well as the BOF and EOF properties, and the
Fields collection. Consequently, the DataControl's result set can be accessed programmatically, in addition to being
accessed by bound controls. Using bound controls is generally not the best approach to developing client/server
applications, however. The nature of bound controls requires that the cursor to which they are bound persists for the life
of the bound controls. For most applications, it is preferable to read the data, populate the necessary controls
programmatically, and close the cursor. Note that in Listing C.2, the Snapshot object is used to populate a Visual Basic
array, and then it is immediately closed, thereby freeing the cursor on the server. In heavily used systems with a large
number of clients, this can have a significant impact on performance.
While the DataControl can be used to perform inserts as well as in-place updates and deletions, it is strongly
recommended to use these methods through ODBC. Unfortunately, when Visual Basic establishes an ODBC connection,
the ODBC AutoCommit connection option is enabled, and Visual Basic does not provide a method to disable this option.
As a result, transaction control is not possible. Even if the transaction involves only a single table and a single operation,
the DataControl is a bad choice for applying transactions. The DataControl always updates every column. If there are
unbound columns, these values must be set manually, which complicates the entire process. For example, if an
application needs to supply a transaction timestamp with every update, there is no clean way to do this. The DataControl
does not support passthrough mode, so the Oracle system variable, sysdate, cannot be supplied as a value. The
application has to supply a time based on the local workstation's clock, which would not only require an assignment to a
member of the Fields collection, it would almost certainly introduce inaccuracies. These same problems apply to the
Dynaset and Table objects because they use the same methods for applying transactions.

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
There are three possible solutions to overcome the AutoCommit problem. One solution would be to use Oracle stored
procedures exclusively for transaction processing. The ExecuteSQL method of the Database object can be used to call
Oracle stored procedures, providing an easy and safe way to communicate transactions to the database. Using stored
procedures also simplifies the development of the client application, by freeing it from the responsibility of generating
dynamic SQL and controlling transactions. The ExecuteSQL method requires a single argument. The argument is a text
string of the SQL to be executed. This method uses passthrough mode by default, so any SQL that can be evaluated by
Oracle can be supplied, including the ODBC syntax for calling Oracle procedures. Listing C.3 demonstrates the use of
the ExecuteSQL method to call an Oracle stored procedure.
Listing C.3. The ExecuteSQL method can be used to call Oracle stored procedures.
Dim iRows As Integer
Dim szStmt As String
szStmt = "{call insert_individual('" & txtLastName.Text & ", '"
szStmt = szStmt & txtFirstName.Text & ", '" & txtNotes.Text
szStmt = szStmt & ", '" & txtDateOfBirth.Text & "')}"
iRows = dbOracle.ExecuteSQL(szStmt)
The ExecuteSQL method returns the number of rows affected by the transaction, and this value can be tested to
determine the success or failure of the operation.
Unfortunately, because Visual Basic does not support prepared execution of SQL statements, it cannot call Oracle stored
procedures that use output parameters, or add parameters to the procedure call. The statement must be built dynamically,
as in Listing C.3.
A second possible means of overcoming the AutoCommit problem is to use a third-party product, such as Oracle Objects
for OLE. This product provides direct replacements for the Visual Basic objects and methods for communicating with
Oracle databases. For example, the following code fragment establishes an Oracle session through VB that can be used
for transaction control:
Dim Session As Object
Dim dbOracle As Object
Set Session = CreateObject("OracleInProcServer.XOraSession")
Set dbOracle = Session.OpenDatabase("ORACLE", "scotty/tiger", 0&)
The preceding example uses the Visual Basic generic (OLE) Object data type and the CreateObject function to request a

new XOraSession object from Oracle's OLE server. The methods of the Session object can then be used to create other
objects, and to manage transactions, using the BeginTrans, CommitTrans, and Rollback methods. A full discussion of
the features of Oracle Objects for OLE is beyond the scope of this appendix. However, it is important to note that it can
provide Visual Basic applications with the capability to control transactions, add parameters to SQL and procedure calls,
and make use of output parameters when accessing Oracle databases, among other things.
The potential shortcoming of the previous two means of bypassing Visual Basic's AutoCommit behavior is that they are
not portable. If a client application needs to access different RDBMSs, these solutions may not be feasible. A third
approach to overcoming the AutoCommit problem is to use the ODBC API directly, which opens numerous possibilities,
including the creation of a truly portable client application.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
A small subset of the ODBC API can be used to provide transaction control in Visual Basic applications accessing
ODBC data sources. The declarations in Listing C.4 should be placed in a module, and will provide access to all
functions needed to connect, apply transactions through embedded SQL, and rollback or commit them, as needed.
Listing C.4. ODBC API declarations for Visual Basic.
Declare Function SQLAllocConnect Lib "odbc.dll" (
ByVal hEnv As Long, hDBc As Long) As Integer
Declare Function SQLAllocEnv Lib "odbc.dll" (
hEnv As Long) As Integer
Declare Function SQLAllocStmt Lib "odbc.dll" (
ByVal hDBc As Long, hStmt As Long) As Integer
Declare Function SQLDisconnect Lib "odbc.dll" (
ByVal hDBc As Long) As Integer
Declare Function SQLDriverConnect Lib "odbc.dll" (
ByVal hDBc As Long, ByVal hWnd As Integer,
ByVal szCSin As String, ByVal cbCSin As Integer,
ByVal szCSOut As String, ByVal cbCSMax As Integer,
cbCSOut As Integer, ByVal f As Integer) As Integer
Declare Function SQLExecDirect Lib "odbc.dll" (
ByVal hStmt As Long, ByVal SQLString As String,
ByVal SQLStringLen As Long) As Integer

Declare Function SQLFreeConnect Lib "odbc.dll" (
ByVal hDBc As Long) As Integer
Declare Function SQLFreeEnv Lib "odbc.dll" (
ByVal hEnv As Long) As Integer
Declare Function SQLFreeStmt Lib "odbc.dll" (ByVal hStmt As Long,
ByVal EndOption As Integer) As Integer
Declare Function SQLSetConnectOption Lib "odbc.dll" (
ByVal hDBc As Long, ByVal fOption As Integer,
ByVal vParam As Long) As Integer
Declare Function SQLTransact Lib "odbc.dll" (
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ByVal hEnv As Long, ByVal hDBc As Long,
ByVal fnType As Integer) As Integer
Global Const SQL_NTS = 3
Global Const SQL_DRIVER_NOPROMPT = 0
Global Const SQL_COMMIT = 0
Global Const SQL_ROLLBACK = 1
Global Const SQL_MAX_MESSAGE_LENGTH = 512
Global Const SQL_AUTOCOMMIT = 102
Global Const SQL_DROP = 1
After establishing a connection for retrieving results with the OpenDatabase method, the application should establish a
second connection to the database using the API functions—for applying transactions. After establishing this connection,
SQLSetConnectOption() should be used to disable AutoCommit. Listing C.5 demonstrates how this might be
accomplished.
Listing C.5. Establishing a database connection using the ODBC API.
Dim hEnv As Long
Dim hDBc As Long
Dim szConnectString As String
Dim iError As Integer
Dim hWnd As Integer

Dim iLenCSOut As Integer
Dim szCSOut As String * 254
szConnectString = "ODBC;DSN=ORACLE;UID=scotty;PWD=tiger;"
hWnd = frmMDIFrame.hWnd
' Allocate environment
iError = SQLAllocEnv(hEnv)
' Allocate connection
iError = SQLAllocConnect(hEnv, hDBc)
' Load driver & connect to ODBC data source
iError = SQLDriverConnect(hDBc, hWnd, szConnectString, SQL_NTS,
szCSOut, 254, iLenCSOut, SQL_DRIVER_NOPROMPT)
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
' Disable autocommit
iError = SQLSetConnectOption(hDB, SQL_AUTOCOMMIT, 0)
Obviously, in practice, the connect string would not be hard-coded and the return value of each function should be
checked. The example in Listing C.5 is intended only to demonstrate the use of the ODBC API to establish a database
connection.
Once a connection has been established, the application can apply transactions using SQLExecDirect() after allocating a
statement handle with SQLAllocStmt(). SQLTransact() can then be used to commit or rollback a transaction based on
the return value of SQLExecDirect(). After applying the transaction, the application should call SQLFreeStmt() to free
the resources allocated to the statement handle. Listing C.6 provides an example of the calls to these functions.
Listing C.6. Applying a transaction using embedded SQL.
Dim hStmt As Long
' Allocate a statement handle
iError = SQLAllocStmt(hDBc, hStmt)
For i = 0 To iStmts
' Apply SQL
iError = SQLExecDirect(hStmt, szSQL(i), SQL_NTS)
If iError Then
' Rollback

iNextErr = SQLTransact(hEnv, hDB, SQL_ROLLBACK)
Exit For
End If
71501,2744Next i
If (iError = 0) Then
' Commit
iError = SQLTransact(hEnv, hDB, SQL_COMMIT)
End If
' Free the statement handle
iError = SQLFreeStmt(hStmt, SQL_DROP)
The example in Listing C.6 assumes that the environment and connection handle, hEnv and hDBc, are valid and
connected to the data source, and that szSQL is an array of iStmts SQL statements. If any statement in the transaction
fails, a rollback is issued and processing of the transaction is discontinued. If all statements are processed without errors,
the entire transaction is committed. Regardless of whether the transaction is committed or rolled back, the application
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
frees the statement handle.
When the application exits, it needs to disconnect from the data source and free all resources allocated to the
environment and connection handles. This can be accomplished with three functions calls, as illustrated in the following
lines:
iError = SQLDisconnect(hDB)
iError = SQLFreeConnect(hDB)
iError = SQLFreeEnv(hEnv)
The full capabilities of ODBC are far beyond the scope of this discussion, but many additional capabilities can be
provided, and an application can be constructed in a manner that is completely database-independent using embedded
SQL. However, if an application does not need to be portable to other database platforms, it may be easier to use stored
procedures or a third-party product, such as Oracle Objects for OLE, to apply transactions from Visual Basic.
It is unfortunate that Visual Basic does not provide a means by which AutoCommit can be disabled for connections
established internally. This is a limitation of the implementation of the Jet Database Engine that Visual Basic uses.
One of the most powerful, (and potentially dangerous), features of Visual Basic is the means by which errors are trapped,
using the On Error statement. This statement is well-used in Visual Basic programming because all untrapped run-time

errors are fatal. The On Error statement is used to specify an error handler to which execution jumps if an error occurs.
The error handler can be specified using GoTo, or Resume Next can be specified to allow program execution to continue
normally. The system variables Err and Error$ store a numeric code and a text description for the error that occurred
most recently. The fact that only the most recent value is stored is what makes On Error Resume Next a somewhat
dangerous way to trap and handle errors. It is very convenient to use, but the value of Err should be checked wherever an
error requiring a specific action occurs.
A common mistake in Visual Basic programming is to misunderstand its array dimensions. Consider the following
declaration, for example:
ReDim szStrings(2) As String
Most would assume that this declares an array of two strings. However, the previous statement actually allocates an array
of three strings. The subscript in an array declaration sets the upper bound, not the number of elements in the array, so
szString(0), szString(1), and szString(2) are all valid, based on the declaration. The base subscript can be changed to 1,
using the Option Base statement:
Option Base 1
If this statement appears in the same module as the previous array declaration, the array would have only two elements,
szString(1) and szString(2). In this case, szString(0) would be an invalid reference.
Summary
The Visual Basic programming language is particularly easy to learn and use. Nearly all developers have some
experience with BASIC, and many of the language constructs have been in place since the very first versions of what is
now referred to as BASIC A.
Despite some idiosyncrasies, a lack of object-oriented features, and relatively weak database support, Visual Basic is an
extremely useful development tool. Its shortcomings are offset by the simplicity of its IDE and language, and extensive
third-party support. The uncluttered and completely non-modal IDE, a flexible ANSI-standard programming language,
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
and a wide variety of pre-built objects make Visual Basic an excellent choice for projects requiring rapid development.
Previous
Page

TOC


Next
Page

Home

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Previous
Page

TOC

Next
Page

Home


D

Using Delphi

Configuring a Data Source

Communicating with the Database

Summary
D
Using Delphi
Borland's Delphi is one of the newest visual development tools for Microsoft Windows. It has positioned itself in the
market as a high performance alternative to Microsoft Visual Basic. What separates Delphi from its competition in this

regard is its ability to compile stand-alone executables and .DLLs, with no run-time libraries required.
Delphi provides full support of ODBC and numerous object classes for communicating with ODBC data sources. It ships
with a single-user desktop version of Borland's InterBase Server and its ODBC-enabled ReportSmith querying and
reporting tool. The client/server edition also ships native IDAPI drivers for Oracle, Sybase, and Informix, among others.
Object Pascal, the programming language used in Delphi development, supports many of the object-oriented features
typically associated with C++, including public and private data members and functions, inheritance, and polymorphism.
The language also supports a wide range of data types and advanced features, including true pointer types and structured
exception handling.
While Object Pascal and Delphi provide an abundance of noteworthy features, this appendix focuses primarily on
Delphi's support for database applications. An overview of the more significant features of the language is included, as
well as a brief discussion of the strengths and weaknesses of Delphi.
Configuring a Data Source
To make a data source available to Delphi, IDAPI.CFG must be edited using the BDE configuration utility. This utility
can be accessed from the Tools menu in the IDE. When the utility is started, it displays currently configured data
sources, some of which may be pre-configured IDAPI interfaces, depending on the options selected when Delphi was
installed. The main window of the BDE configuration utility should appear as shown in Figure D.1.
Figure D.1. The main window of the Delphi BDE configuration utility.
Delphi can interface with any ODBC data source configured and defined in ODBC.INI. To configure an existing ODBC
data source for Delphi's use, click on the New ODBC Driver command button. A simple dialog box displays and
requires three basic items. The SQL Link Driver name is simply an identifier to be used by Delphi, and any unique name
can be used. Using the Default ODBC Driver drop-down, select from the list of currently installed drivers. If the required
driver does not appear in the list, it has not been correctly installed. ODBC drivers can be installed using ODBCADMN.
EXE, a Microsoft utility that is typically provided by driver vendors. After selecting a driver, the Default Data Source
Name drop-down will be populated with a list of the data sources defined for the selected driver. Figure D.2 provides an
example of how this dialog box might look when configuring the Oracle7 ODBC driver.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Figure D.2. The Add ODBC Driver dialog box can be used to set up the Oracle7 driver for use in Delphi.
After selecting the default data source, click on the OK button to add the driver. The newly added driver should now
appear in the main window of the configuration utility, as shown in Figure D.3.
Figure D.3. Parameters can be set from the main window of the configuration utility.

Of the available parameters, SQLQRYMODE and SQLPASSTHRU MODE are the most significant. Setting
SQLQUERYMODE to SERVER causes Delphi to deliver all queries to the database server for processing. This prevents
the local BDE from attempting to process the query from the desktop. The SQLPASSTHRU mode should be set to
SHARED NOAUTOCOMMIT. This setting prevents transactions sent to the server from being committed
automatically. The tabs across the bottom of the BDE configuration utility's main window allow the setting of additional
parameters, including date, time, and numeric formats. These settings apply to the local database engine and should not
need to be modified for ODBC connections.
The BDE configuration utility supports multiple configuration files. The changes can be saved in a new file, and the
utility will optionally update WIN.INI to use the new file as the default. When adding a new data source, you may want
to save the configuration file with a different name so that the old file can be used as a backup, in case other data sources
were changed inadvertently. After the configuration file is saved, Delphi can access the new data source.
Communicating with the Database
Delphi supplies several classes that can be used to establish and maintain connections to the database, process
transactions, and retrieve result sets. The most useful of these classes include:

TSession—Used to manage database connections

TDatabase—Used to establish a connection and control transactions

TQuery—Subclass of TDataSet, used to retrieve results and apply transactions

TStoredProc—Subclass of TDataSet, used to call stored procedures

TDataSource—Used to bind data-aware controls to result sets and to link related result sets

TField—Corresponds to a column in a result set
All of these objects are available on the Data Access tab of the toolbar.
Communication with the database is initiated by the TDatabase component. Before establishing a connection, the
DriverName and DatabaseName properties must be set. These should correspond to the driver name (specified when the
data source was set up through BDE Configuration) and a valid data source name for the specific driver. The application

connects to the database by using the Open method, or by setting the Connected property to True.
While all objects' properties and methods can be accessed through the object inspector, some objects and
properties have special dialog boxes. For example, double clicking on the TDatabase component displays a dialog box
that makes it easier to view and modify its properties.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
If the Params property does not have all required parameters set, the TDatabase object displays a login form
automatically, provided that the LoginPrompt is set to True. Connection parameters are set using the Params property,
which is actually a TStrings object. Parameters are set using the Add method, as demonstrated in the following code
fragment:
dbOra.Params.Add('SERVER NAME=ORACLE');
dbOra.Params.Add('USER NAME=scotty');
dbOra.Params.Add('PASSWORD=tiger');
dbOra.Connected := True;
The KeepConnected property, when set to True, keeps the connection open even when no result sets are open and no
transactions are in progress. The TransIsolation property determines how records are read when they are currently
involved in a transaction. Oracle supports tiReadCommitted, which allows only committed changes to be read, and
tiRepeatableRead in read-only mode. The latter mode prevents Oracle from attempting to refresh the Snapshot for
records that have already been read.
The TDatabase object is also used for transaction control, (assuming that SQLPASSTHRU MODE was set to SHARED
NOAUTOCOMMIT when the data source was configured.) The StartTransaction, Commit, and Rollback methods
should be used whenever SQL transactions are applied. The exception to this rule is when the application calls a stored
procedure which handles commits and rollbacks internally.
In some cases, the behavior of the TDatabase object can be overridden by the TSession object. The TSessions object is
particularly useful for applications that connect to more than one database or need more than one connection for
asynchronous processing. The KeepConnections property of the TSessions object will override the KeepSessions
property of individual TDatabase objects. The TSession object is always global and has additional methods for querying
the BDE configuration files. It stores an array of connected TDatabase objects in its Databases property, and the number
of connected TDatabase objects in its DatabaseCount property.
The TQuery object is the primary means for communicating with the database. It is used not only to retrieve results, but
also to apply SQL transactions. The SQL property is a TStrings object used to store the SQL to be executed. An

application can use a TQuery to build simple dynamic SQL very easily, using the Params property. Application variables
can be substituted for literal values wherever they might appear. For example, the following statements can be used to
generate a result set dynamically based on a value entered by a user in a TEdit object:
qryGetCusts.SQL.Add('SELECT * FROM CUSTOMERS WHERE LAST_NAME LIKE :LastName');
qryGetCusts.Params[0].AsString(txtLastName.Text);
This is particularly useful for data entry forms where records will be inserted frequently into the same table or view. The
Params property is an array of TParams objects. They are referenced by subscripts and set using the AsString, AsInteger,
AsFloat, or one of the other properties that store a value based on a data type.
When SQL is parameterized, it should be prepared only once. It can then be executed repeatedly with different
parameters. The code fragment in Listing D.1 illustrates the use of the Prepare and ExecSQL Methods, as well as
transaction control provided by the TDatabase object.
Listing D.1. Dynamic SQL and transaction processing in Delphi.
{this code is executed once}
qryAddCust.SQL.Add('INSERT INTO CUSTOMERS (LAST_NAME, FIRST_NAME) ');
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
qryAddCust.SQL.Add('VALUES (:Last, :First)');
qryAddCust.Prepare;
{this code can be executed many times}
dbOracle.StartTransaction;
qryAddCust.Params[0].AsString(txtLast.Text);
qryAddCust.Params[1].AsString(txtFirst.Text);
try
qryAddCust.ExecSQL;
dbOracle.Commit;
except
dbOracle.Rollback;
end;
The ExecSQL method should be used only for statements which do not return a result set. For SELECT statements, the
Open method should be used. After a result set has been returned from the database, the TQuery object will have an
array of TFields objects, which can be accessed in much the same way as TParam objects. Listing D.2 demonstrates the

methods used to retrieve result sets from a TQuery object.
The right mouse button has special uses for many objects. Right-clicking on a TQuery object displays a
pop-up menu that you can use to access two dialog boxes that are specific to designing a TQuery. You can use the Fields
Editor and Define Parameters dialog boxes to build queries visually.
Listing D.2. Using the query object to retrieve results without bound controls.
qryGetCusts.Open;
while (qryGetCusts.EOF = False) do
begin
lstFullName.Add(Concat(qryGetCusts.Fields[0].AsString, + ','
+ qryGetCusts.Fields[0].AsString));
qryGetCusts.MoveBy(1);
end;
qryGetCusts.Close;
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
qryGetCusts.SQL.Clear;
Several important properties and methods that apply to the TQuery are illustrated by Listing D.2. The EOF and BOF
properties are Boolean values, set to true when the record pointer is positioned at end-of-file or at beginning-of-file,
respectively. The properties AsString, AsInteger, and so on apply to the TFields objects in much the same way as they
are applied to TParam objects, only they are typically used to read, rather than set values. MoveBy can be used to move
the record pointer any number of records in the current direction, (which is forward by default). First, Last, Prior, and
Next can also be used to position the record pointer. The Close method should always be called when the result set is no
longer needed, and the Clear method of the SQL property should be used to clean up when an application sets this
property dynamically at run time.
There are several other important TQuery properties that are not demonstrated by the code example in Listing D.2. The
RequestLive property can be used to create a cursor that is updatable and refreshes automatically. However, there are
numerous restrictions that can prevent this property from being used. In most cases, this should be set to False even
when it is supported because it is likely to increase network traffic and database contention. The UpdateMode property is
applicable only to live result sets and is used to set the requirements for matching records on updates. When an
application does not need scrollable cursors, the Unidirectional property should be set to True. This improves
performance, particularly when scrollable cursors are not supported by the driver. In these cases, Delphi attempts to

emulate a scrollable cursor, potentially causing it to read the entire result set before returning from the Open method.
One property that should not be overlooked is the Database property. This value must correspond to the Database
property of a connected TDatabase object. The Database property should not be confused with the DataSource property,
which is used to locate unbound parameters at run-time through a TDataSource object, which can be very useful in
creating relationships between queries.
The TDataSource component is used to bind data-aware controls to result sets. Delphi includes a full complement of
data-aware controls, including a grid, radio buttons, check boxes, lists, and drop-down combos. The DataSet property of
the TDataSource object is used to specify the query or table that it will use to bind results to controls. The AutoEdit
property is applicable only to live results sets. When set to True, this property forces the underlying result set into Edit
mode when a bound control is modified. When communicating with remote database servers, using the AutoEdit
property for updating records is generally unacceptable, because transaction control is lost.
Bound controls are most useful in displaying read-only result sets or when used in conjunction with a separate query to
apply an update to an existing record. A record can be retrieved and displayed with minimal code using bound controls,
but in order to use the transaction methods of the TDatabase component, code must be written to apply any updates
using a separate query.
A TDataSource component does not have its own set of TField objects—it simply accesses an existing result set to
supply information to bound controls. Controls are bound to a TDataSource through the DataSource and DataField
properties. The DataSource must correspond to an existing TDataSource object's name, and the DataField property
should correspond to a column in the underlying query or table. If these properties are set correctly, bound controls are
populated automatically when the TQuery or Ttable is opened and the TDataSource is enabled.
The simplest way to implement this technique requires no code at all. A TTable object can be placed on the form with its
Database and TableName properties set and its Active property set to True. Next, a TDataSet object is placed on the
form, and its DataSet property is set to the name of the TTable object. Finally, a data-aware TDBGrid is placed on the
form, and its DataSource property is set to the name of the TDataSource object. At run-time, as soon as the application
connects to the database, the table is read, and the grid is populated. This simplicity is one of the great attractions to
using bound controls.
However, the use of bound controls is a questionable technique, particularly in MDI applications. In order for bound
controls to display data, the query or table object must remain open; this prevents the object from being used to apply
other transactions or queries. If you have several MDI windows concurrently open with bound controls, using multiple
TQuery or TTable objects can become very expensive in terms of workstation, network, and server resources. Although

it requires more handwritten code, using a query to populate controls and closing the query immediately is a safer and
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
more efficient approach.
A better use of the TDataSource object involves the creation of dynamic SQL. In addition to accessing data from TQuery
and TTable objects, the TDataSource object can be used to provide parameters to TQueries at run-time. The DataSource
property of the TQuery object is used to specify the name of a TDataSource to check when attempting to resolve
unbound parameters. If the name of an unbound TQuery parameter matches a column name in a TDataSource data set,
the value of the matching column at the current record position will be used as the parameter. For example, an accounts
receivable system may need to display summary records of outstanding accounts and also provide transaction details as
requested. This can be accomplished using two TQueries and a TDataSource that use information supplied at run-time.
One TData query might provide the summary level information in a read-only data-aware grid, including the account
number. Right-clicking on a particular account might be used as the mechanism to display a pop-up with the account
details for the selected account. The SQL for the TQuery object used to retrieve the detail information might be defined
at design-time as:
SELECT DATE, DEBIT_AMT, CREDIT_AMT
FROM TRANSACTIONS WHERE ACCT_NO = :acct_no
ORDER BY DATE
A TDataSource object can be used to supply the parameter value to this query. The DataSet property of the TDataSource
object should be set to the summary-level TQuery, which uses the SQL below:
SELECT ACCT_NO, ACCT_NAME, BALANCE
FROM ACCTS_REC
ORDER BY ACCTS_REC
The TQuery object should then set its DataSource property equal to the name of the TDataSource object. When the detail
TQuery is opened, it will receive the value of ACCT_NO for the current summary record as the parameter to its SQL
with no intervention at run-time. The TDataSource object is particularly useful in creating this kind of master-detail
relationship.
Delphi also provides support for stored procedures through its implementation of the TStoredProc component. As with
the other descendants of TResultSet, the TStoredProc object requires the name of a connected TDatabase object in its
Database property. The name of the stored procedure in the database should be selected from the drop-down list
provided for the StoredProcName property. The ParamBindMode is used to indicate how parameters will be bound at

run-time, by name or by index. The Active property should be set to False unless the stored procedure will return a result
set. If this is the case, setting the Active property to True will cause the stored procedure to fire.
The TStoredProc component is ideal for applying parametered transactions on data entry forms. Stored procedures are
executed in much the same way as SQL transactions applied through TQuery objects. Listing D.3 provides an example
of using a stored procedure to insert records from a simple data entry form.
Listing D.3. Demonstrating the use of the TStoredProc component.
{This code should only be executed once,
perhaps in the form's constructor }
spInsIndiv.Prepare;
{This code will be executed for each transaction}
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
spInsIndiv.ParamByName('Last').AsString := txtLast.Text;
spInsIndiv.ParamByName('First').AsString := txtFirst.Text;
spInsIndiv.ParamByName('DOB').AsString := txtDOB.Text;
spInsIndiv.ParamByName('Notes').AsString := txtNotes.Text;
try
spInsIndiv.ExecProc;
Application.MessageBox('A new record was added.',
'Oracle Unleashed', mb_OK)
except
Application.MessageBox('Unable to add the current record.',
'Oracle Unleashed', mb_OK);
end;
Note that the method for executing a stored procedure, ExecProc, is different than the method used to execute a query.
As with dynamic SQL queries, the procedure needs to be prepared before parameters are bound, but only once.
Assigning values to parameters using the ParamByName method works in much the same way as accessing the array of
TParams by index. Using ParamByName is probably a better choice because it results in more readable code.
The parameters to stored procedures can be viewed at design-time by right-clicking the TStoredProc
object and selecting Define Parameters from the pop-up menu.
When calling stored procedures with output parameters, you must allocate sufficient space for variables that will be

bound to the output parameters. This is a potential problem when using string variables. For example, the following lines
of code will result in a general protection fault:
var
PChar: szOut;
begin
spGetNotes.Prepare;
spGetNotes.ParamByName('NotesOut').AsString := szOut;
The next code fragment should work, (assuming that the length of the string returned from the database will never
exceed 255 bytes):
var
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
String: szOut[255];
begin
spGetNotes.Prepare;
spGetNotes.ParamByName('NotesOut').AsString := szOut;
When using stored procedures, explicit transaction control may not be necessary. In most cases, any commits or
rollbacks should be handled within the stored procedure. This is the safest way to process transactions, and it simplifies
the code that applies transactions from the client side.
The methods of communicating with the database described in this section are a small subset of all the options provided
by Delphi. Additional database components include the TBatchMove object and the TReport object, which allows
ReportSmith reports to be integrated with Delphi applications.
Summary
As mentioned in the introduction to this appendix, the object-oriented features, support for pointers and complex data
types, and language structure provided by Object Pascal are among Delphi's greatest strengths.
In addition to the standard data types, Object Pascal allows the creation of any type of pointer, including pointers to user-
defined classes and structures, (records, in Pascal terminology). Pointers are allowed within complex data types and class
definitions, as well.
Class declarations in Object Pascal are very similar to C++ declarations. The ability to declare member data elements
and private functions improves encapsulation and allows for information hiding. Also in the C++ tradition, Delphi's
model of inheritance allows base class functions to be overridden by descendants, allowing polymorphic behavior in

user-defined classes. Unfortunately, support for overloaded member functions seems to be lacking.
Another strong feature of Object Pascal is its support for structured exception handling. Listings D.1 and D.3 provide
examples of the try..except blocks used to handle exceptions in Delphi. The finally keyword can be used to create a
resource protection block that frees memory without handling the exception. The code listings in D.1 and D.3 do not
actually handle the exceptions, either. Delphi provides a fairly comprehensive list of pre-defined exceptions that can be
handled using the syntax:
on exception do statement
An exception instance is never destroyed until it is handled. User-defined exceptions can also be declared and thrown.
Delphi's weaknesses are few, considering that it is a first version of a fairly complicated development tool. Aside from
the occasional UAE and some bugs in the security features of the local database server, Delphi seems to be a stable and
solid product. The database objects, while easy to use, do seem to be geared more to desktop applications than client/
server development, with heavy emphasis on bound controls. However, SQL Traces will show that it makes very
efficient use of the ODBC API in certain situations. This is because it maps to the API more cleanly than most Windows
development tool's ODBC layers, which typically don't provide methods for using prepared execution. The close
relationship to ODBC is somewhat surprising considering that IDAPI drivers are shipped with the product. For
developers that are already familiar with the ODBC API, this should make a switch to IDAPI nearly transparent.
Overall, Delphi goes a long way toward living up to its billing as "the RADical performance tool". The IDE is fairly
intuitive, the object hierarchy well-designed, and the Object Pascal programming language is arguably the best among
the desktop development tools. The easy-to-configure ODBC connectivity and support for stored procedures make it a
good choice for developing client applications to interface with Oracle.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Previous
Page

TOC

Next
Page

Home


Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Previous
Page

TOC

Next
Page

Home


E

Oracle Power Objects Programming Reference

Overview of Oracle Basic

Supported Data Types

Variables

Constants and Reserved Words

Operators

Expressions

Statements


Commands

Database Interaction Commands

Definition and Manipulation of Variables or Constants

Definition of Functions and Subroutines

Directory and File Management Commands

File Input and Output Commands

Miscellaneous Commands

Program Flow Commands

Functions

Aggregation Functions

Array and Subscript Functions

Data Type Conversion Functions

Date Functions

File Input and Output Functions

Financial Functions


General Functions

Mathematical Functions

Selection Functions

SQL Functions

String Functions

Test Functions

User Dialog Functions

Summary
E
Oracle Power Objects Programming Reference
Power Objects uses Oracle Basic as its programming language. Oracle Basic is fully compatible with ANSI BASIC with
the additions of object concept extensions and Structured Query Language (SQL) extensions. Oracle chose Basic as the
programming language in order to appeal to the widest developer experience and level. This chapter discusses the
majority of all commands and functions comprising Oracle Basic in order to provide you with a reference on syntax and
purpose.
Overview of Oracle Basic
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Oracle Basic is fully compatible with ANSI BASIC and is therefore compatible with Microsoft Visual Basic. You are
not required to write Basic code in order to use Power Objects effectively. If you find that the default functionality of a
method or process does not exactly meet your requirements, then you can overwrite, modify, or augment the default
behavior using Oracle Basic code.
Using Oracle Basic you can create user-defined functions and methods. You can declare variables and external

functions. You can obtain direct access to the data contained in visual and non-visual objects. Oracle Basic provides the
developer with the capability to create the features in the Power Objects product that he finds are missing or incomplete.
Supported Data Types
Power Objects provides support for two different data type concepts: database-related data types and Oracle Basic-
related data types. Database-related data types are the values that you specify when you are defining a new table in the
Table Editor or when using SQL statements such as CREATE TABLE. These data types specify the type of data that is
stored in the table column. Database data types vary depending on the database you use. The following lists show the
data types for three different types of databases.
Blaze Oracle7 SQL Server
CHAR CHAR BINARY
DATE DATE BIT
FLOAT FLOAT CHAR
INTEGER LONG DATETIME
LONG LONG RAW FLOAT
LONG RAW MLSLABEL IMAGE
MLSLABEL NUMBER INT
NUMBER ROWID MONEY
ROWID VARCHAR REAL
VARCHAR VARCHAR2 SMALLDATETIME
VARCHAR2 SMALLINT
SYSDATE
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.

×