Está en la página 1de 20

Your Free Issue!

April 2008
Number 2
Dear FoxPro Developer, 2 ADS Special Issue
Advantage Database
Here is your free copy of the new magazine dedicated to FoxPro, Server for Visual
FoxRockX! This is not a regular issue but a sample issue to show you FoxPro Developers
how FoxRockX looks like. And we are sure you will like it!
As you know the FoxPro community is alive, well and keeps going. Doug Hennig
And the roughly one hundred thousand active users of FoxPro with
their amazingly large number of FoxPro applications in production are interesting to other companies. The
lack of marketing at Microsoft for Visual FoxPro does not mean that other companies are not allowed to ex-
tend their products to work together with Visual FoxPro and make interesting offers to the FoxPro commu-
nity.
One of these offers is the new version 9.0 of Advantage Database Server from Sybase (formerly Extended
Systems). The first feature mentioned in the press release published March 17th, 2008 consists in the unique
support for the Visual FoxPro file format, see http://www.sybase.com/detail?id=1056446. Now you can
work with DBF tables on the server with no direct access to the tables by the end user, no damaged index
files, and you can even use ODBC. Doug Hennig took a close look at the pros and cons from a VFP perspec-
tive.
This free special issue of FoxRockX contains all the details you need to know. And we are also going to pub-
lish a localized German edition of this special issue, available for free as well. If you want to start working
with the trial version of Advantage Database Server afterwards, you can view a webcast about how to set up
everything at http://devzone.advantagedatabase.com/jeremym/fox1/fox1.html.
We are committed to providing the best dedicated magazine for FoxPro Developers worldwide. Our list of
authors includes many names already familiar to you and we will be actively encouraging contributions
from new authors. Great articles from great developers will help you hone your skills and increase your
productivity. This issue just gives you an idea of the quality of our articles. Besides that we have no business
connection with Sybase except that they sponsored this issue. If you like FoxRockX, take action and:
Join FoxRockX today at our affordable rates (which include access to one of the world's largest online ar-
chives of articles on Visual FoxPro). We already have more than a thousand subscribers for this first issue
and we hope that you will help to spread the word and help us expand the readership even further. After all,
the wider our membership, the more material we will be able to provide. Here are the details:
FoxRockX is published bimonthly with 24 pages DIN A4 (same size as FoxTalk) and all subscriptions in-
clude access to the complete online archive of FoxTalk as well as of FoxRockX. Sometimes we add a free is-
sue (with sponsored articles) like this one - at least once a year and possibly more often. The Annual On-Line
Subscription is US$ 99.00 / 75.00 EUR and the Annual On-Line and Printed Copy Subscription is US$ 158.00
/ 109.00 EUR. Note: If you are a former subscriber of FoxTalk 2.0 you can upgrade your existing on-line sub-
scription to the printed version by paying only the difference (US$ 59.00/ 34.00 EUR).
For more details visit our small new homepage at: http://www.foxrockx.com. To subscribe now, visit either
http://shop.dfpug.com (Europe/Asia) or http://www.hentzenwerke.com (USA/Canada). On-line articles,
archives and companion materials will be accessible through the "FoxRockX" tab at http://portal.dfpug.de
(Access information will be sent with the confirmation of your subscription).

Page 1 FoxRockX April 2008


Advantage Database
Server for Visual FoxPro
Developers
Doug Hennig

Advantage Database Server is a full-featured, cally designed to meet the needs of business ap-
high-performance client/server database engine. plication developers. The more you read about
Interestingly, it can use Visual FoxPro DBF files as ADS, the more you realize that its features align
its data store and provides a number of benefits very nicely with those of the database engine in
over accessing these files directly. This article in- Visual FoxPro. However, it doesnt replace VFP.
troduces Advantage and discusses how to access Like SQL Server, ADS is a database engine rather
it from VFP applications. than a full-featured programming language, and
you can easily access its data in VFP using ODBC
Introduction or ADO. However, as you will see, ADS has better
Visual FoxPro is a wonderful development tool. support for VFP than any other database engine,
Its rich object-orientation, powerful language fea- and its latest incarnation, version 9, greatly ex-
tures, integrated report writer, and open and ex- tends this support.
tendible interactive development environment Heres an overview of the features of ADS
(IDE) make it one of the best tools available for compared to VFP:
developing desktop applications. However, its
Its a true client/server database engine. With
built-in data engine is both one of its greatest
file-based engines like VFP, the server con-
strengths and greatest weaknesses. Strength be-
taining the data files is just a file server. All
cause the data engine is tightly integrated into
processing, such as selecting records, is per-
VFP and is one of the fastest on the planet and
formed on the workstation, so the entire table
weakness because the DBF file structure can be
must be brought down from the server. With
subject to corruption, lack of security, and size
client/server engines, all processing is done
limitations. Fortunately, VFP developers arent
on the server, so only the results are sent to
restricted to only using VFP tables as their data
the workstation. This provides several bene-
store; VFP makes a great front-end to cli-
fits, including reduced network traffic and
ent/server databases such as SQL Server, Oracle,
more database management capabilities. In
and Sybase.
addition, the engine is multi-threaded and
This article discusses another product in the supports multiple processors for better scal-
client/server database market: Advantage Data- ability.
base Server. It first looks at what Advantage Da- ADS actually comes with two database en-
tabase Server is and what features it has, then gines: local and remote. The local engine isnt
delves into how to access Advantage from VFP a true database server but more like VFP in
applications. For the sake of those who are rela- that it uses file-based access to the data. It
tively new to client/server technologies, this arti- uses an in-process DLL that loads into the
cle assumes you dont have much experience with ODBC driver on the clients machine. The re-
accessing backend databases and goes into some mote engine is a true database server that
detail on how to do that. provides all of the benefits of a client/server
architecture. Advantage Local Server is useful
Introducing Advantage Database for testing on a single development system or
Server as a low-cost database engine (its actually
Advantage Database Server, or ADS, is from Sy- free) for commercial applications, but has
base iAnywhere, a subsidiary of Sybase. Accord- many significant limitations the Advantage
ing to their marketing materials, Advantage Da- Remote Server doesnt have. The benefit of
tabase Server is a full-featured, high performance Advantage Local Server is that it gives you a
client/server data management system specifi-

April 2008 FoxRockX Page 2


client/server-like mechanism you can scale Like VFP, ADS tables can be free or enhanced
up to the full remote server if necessary. with a data dictionary (an ADD file). Similar
The remote server can be accessed over the to the VFP database container (DBC), ADD
Internet if necessary. It supports encrypted files dont contain the tables but instead
and compressed data transmission for secu- provide additional information, or meta data,
rity and performance. about them. Advantages data dictionary
One of the most interesting things about ADS maps very closely to the VFP DBC, including
is that it can use either a proprietary file for- things such as long field names, primary keys,
mat (files with an ADT extension) or DBF files referential integrity rules, default field values,
for data storage. While there are benefits to field validation rules and custom error mes-
using ADT files, including additional data sages (although ADS only supports minimum
types DBFs dont support, using DBFs makes and maximum or null values rather than ex-
it easier to migrate an existing VFP applica- pressions which can do any type of valida-
tion to a client/server model. Whats really tion), table validation rules and custom error
interesting about this is that you can access messages, views, triggers, and stored proce-
your existing DBFs through ADS to take ad- dures. As this article discusses later, ADS
vantage of the features ADS provides while comes with a utility that generates an ADD
still accessing them directly as VFP tables. from a DBC, automating most of the effort in
This makes a very attractive migration strat- creating an Advantage data dictionary.
egy: you can modify your application module Although ADSs documentation uses the term
by module to use client/server techniques Advantage optimized filters, the descrip-
while older modules continue to work un- tion of the technology that provides the high
changed. performance querying capabilities of ADS
When accessing DBF files, it supports two sounds just like the Rushmore technology that
locking mechanisms: compatible and proprie- gives VFP its speed. ADS examines an index
tary. Compatible locking, which uses operat- to determine which records match the filter
ing system locks on bytes in the DBF files, al- conditions, only accessing the physical re-
lows simultaneous access to the data by ADS cords when an index isnt available. The ADS
and non-ADS (such as VFP) applications. documentation has terms like fully opti-
Proprietary locking uses an internal locking mized and partially optimized just like the
mechanism. This provides better stability and VFP documentation. This means VFP devel-
control but means that the files are opened opers can use their existing knowledge of op-
exclusively by ADS and cannot be access by timizing VFP queries with ADS databases.
another application until they are closed in ADS has a full-text search engine providing
ADS. very fast searches of memo files. Many VFP
It provides database security. A valid user developers use third-party products such as
account is required to connect to the database PhDbase for full-text searching in their appli-
so unauthorized users cannot access your cations, but some of these tools are no longer
data. Different user accounts can have differ- available or havent been upgraded to work
ent levels of permissions. For example, its with the latest versions of VFP.
unlikely normal users need to alter, create, or Although ADS can access DBF files, it doesnt
drop tables, so you can prevent everyone but have the same limits that VFP does. For ex-
administrative users from performing these ample, in VFP, DBF and FPT files are limited
database-altering tasks. Even if youre using to 2 GB. In ADS, there isnt a direct limit on
ADS with DBF files, you can place these files the size of the file; instead, the limit is a
in a folder on the server that normal users maximum of 2 billion (2,147,483,648) records.
dont have access to, so the only access they Of course, if your DBF becomes larger than 2
have to the data is through ADS. This would GB, youll only be able to access it through
be a lot more difficult to implement using a ADS since VFP will see it as invalid.
purely VFP solution. Since the ADS ODBC driver fully supports
For additional security, ADS can encrypt the VFP 9 data types, you can use it in place of
tables using a case-sensitive password. Doing the VFP ODBC driver, which hasnt been up-
so in purely VFP solution requires a third- dated since VFP 6 and so doesnt support new
party product such as Cryptor by Xitech and features like Varchar, Varbinary, and Blob
managing the access to the encrypted data fields.
yourself. ADS supports transactions, complete with
commit, rollback, and automatic rollback if

Page 3 FoxRockX April 2008


the workstation or server crashes during the to use. Once youve answered these questions, the
transaction. Advantage Configuration Utility opens (see Fig-
Replication is a process that distributes ure 1), allowing you to see statistics about the
changes in records in the tables of one data- server, including the number of users and connec-
base to the tables of another
database, such as changes
made in databases in remote
offices to a single
consolidated database at head
office or vice versa.
Replication with VFP data is
certainly possible but you
have to write the code
yourself, deal with all types of
issues such as conflict
resolution, and test it exten-
sively to ensure it works
under all conditions. ADS has
built-in replication features so
theyve done all the hard
work for you.
ADS includes online backup
capability, meaning you can
back up your tables while
theyre open in an
application. It isnt possible to
do that using normal backup
procedures against VFP Figure 1. After installing ADS, the Configuration Utility appears, allowing you to config-
tables. You can perform full ure the server properties.
or incremental backups.
tions, and configure certain properties, such as
timeout, ports used, and log file locations.
Installing Advantage Database Server The next thing to install is the Advantage
There are several components that make up Ad- Data Architect, an ADS utility discussed in the
vantage: the database server itself, the Advantage next section. Its installer is called Arc32.EXE. Like
Data Architect, the ODBC driver, and the OLE DB the server installation, you can specify the install
provider. At the time this article was written, ADS folder name, the ANSI character set, and OEM
version 9 was in beta and available for download character set. Next, install the ODBC driver by
at http://devzone.advantagedatabase.com. The running ODBC.EXE if you plan on using ODBC to
release version is due out in March 2008 and the access ADS and install the OLE DB provider by
beta will expire at the end of that month. running OLEDB.EXE (you should install this re-
ADS runs on Windows, Netware, and Linux. gardless of whether you plan on using ADO or
For Windows, the name of the installer for the not because OLEDB.EXE installs a VFP-specific
database server is NT.EXE. Run this program on utility discussed later in this article). Both of these
the server you want ADS installed on. You can, of prompt for the install folder name, the ANSI
course, install it on the same system you do de- character set, and OEM character set.
velopment on rather than a separate server, but Youre now ready to start using ADS.
you would normally install it on an actual server
in a production environment. By default, the en- Advantage Data Architect
gine installs into C:\Program Files\Advantage 9.0 Advantage Data Architect, also known as ARC, is
(this is also the default for the other components). a connection and database management tool for
After installing the engine files, the installer ADS. If youve used SQL Server Enterprise Man-
prompts you for the name of the registered ager or Management Studio, or even the VFP Data
owner, whether the engines Windows service Explorer, this utility will be somewhat familiar.
should be started automatically or manually (the Interestingly, the complete source code for ARC,
default is automatic), which ANSI character set to which was written in Delphi, is included with the
use (the default is to use the default setting for the utility. ARC is shown in Figure 2.
machine), and which OEM/localized character set

April 2008 FoxRockX Page 4


Figure 2. Advantage Data Architect provides many of the same features as the VFP Data Explorer or SQL Server Management Studio.

ARC has functions to: This includes support for long field names, pri-
Create, maintain, and delete databases and mary keys, referential integrity, field and table
tables validation, triggers, and so on; in other words, the
Browse tables with filtering, searching, sort- same things the VFP database container is used
ing, and navigation for. In anything but a small database, it would be
quite a bit of work to create an Advantage data-
Import and export data
base for an existing VFP database. Fortunately,
Export table structures as code ADS comes with a utility written in VFP, ADSUp-
Manage security settings and user accounts size.PRG, which creates an Advantage database
Execute queries in the SQL Utility and Query and populates it with information about the tables
Builder tools in a VFP database. ADSUpsize.PRG is somewhat
Compare data dictionaries misnamed since it doesnt upsize the VFP DBC
or make any changes to the DBF files. In the re-
The left pane in ARC is the Connection Re- lease version of ADS 9, Sybase iAnywhere has
pository. This provides easy access to ADS data- renamed the utility to DBCConvert.PRG.
bases youve registered with ARC. (A database
To see how ADSUpsize.PRG works, upsize
doesnt have to be registered with ARC to use it in
the Northwind sample database that comes with
other applications.) To create a database and add
VFP. Start by creating a new folder and copying
it to the repository, choose Create New Data Dic-
all the files in the Samples\Northwind folder of
tionary from the File menu; this creates an empty
the VFP home directory to it; that way, you wont
ADD file with the properties you specify in the
alter your original database when you make some
dialog that appears. To add an existing database
changes described later. Start VFP and run the
or a directory of free tables to the repository,
copy of ADSUpsize.PRG included in the source
choose New Connection Wizard from the File
code accompanying this article; it contains some
menu and follow the steps in the wizard dialog.
changes to the original program shipped with the
Youll use various functions in ARC through- beta described in the Fixing the ADS VFP upsiz-
out the examples in this article. ing utility sidebar. When prompted for the data-
base, select Northwind.DBC in the folder you cop-
Upsizing a VFP database ied the files to. After a few seconds, the program
Although ADS 9 supports most VFP data features, completes its tasks. However, note the message
some of this support relies on using an Advantage displayed: there were 15 errors, but it doesnt in-
database rather than free tables. (From an ADS dicate what they are. Fortunately, ADSUp-
point-of-view, even tables in a VFP DBC are free size.PRG logs the upsizing process, as discussed
tables if they arent included in an ADS database.) later.

Page 5 FoxRockX April 2008


Check the directory containing the North- Tables node but only five of the seventeen views
wind database and youll see some new files: appear under Views. In addition, there are the
Northwind.ADD, AI, and AM: The ADS da- four new tables mentioned earlier, FieldUpgrad-
tabase. eResults, RelationsUpgradeResults, TableUpgrad-
BAK versions of every DBF and FPT: The up- eResults, and ViewUpgradeResults.
sizing process (not ADSUpsize.PRG but ADS Open TableUpgradeResults by double-
itself) backs up these files just in case. clicking it. Each upsized table is listed multiple
FieldUpgradeResults.ADM and ADT, Rela- times, once for each operation. The various col-
tionsUpgradeResults.ADM and ADT, Table- umns in this table indicate what process was per-
UpgradeResults.ADM and ADT, and formed in each step. For example, for Customers,
ViewUpgradeResults.ADM and ADT: These there are records for adding the table to the ADS
ADS tables contain log information about the database, specifying long field names, creating
upsizing process, including any errors that indexes, specifying the table validation expression
occurred. Youll use these extensively to find and message, and defining the primary key. Note
and resolve problems in the upsizing process. that one table, Categories, has an error in the step
FAIL_EMPLOYEES.DBF and FPT: Discussed specifying long field names. The error message is
later. (edited for space):

The requested operation is not legal for the


Open ARC and choose New Connection Wiz- given field type. ALTER TABLE CATEGORIES
ard from the File menu or click the New Connec- ALTER "CATEGORYID" "CATEGORYID" autoinc NOT
tion Wizard button in the toolbar. Choose Create NULL ALTER "CATEGORYNA" "CATEGORYNAME" char(
15 ) NOT NULL ALTER "DESCRIPTIO"
a connection to an existing data dictionary in the "DESCRIPTION" memo NULL ALTER "PICTURE"
first step of the wizard. In step 2, specify a name "PICTURE" blob NULL
for the database and the path to Northwind.ADD,
the Advantage database created by the upsizing Open FieldUpgradeResults. This table shows
utility. Leave the other settings at their default the results of upsizing field comments, validation
values and click Finish (see Figure 3). ARC asks messages, and default values. Again, one record
you to login as the AdsSys user (the default ad- has an error indicating the upsizing utility
ministrative user name); since theres no pass- couldnt set the comment for Categories.Cate-
word for that user in this example, click OK. goryName; that field doesnt exist in the data dic-
All the tables in Northwind appear under the tionary because the error logged in TableUp-

Figure 3. Use the New Connection Wizard to create a connection to your upsized VFP database in Advantage Database Architect.

April 2008 FoxRockX Page 6


gradeResults prevented defining the long field ARC (right-click the table and choose Proper-
names for Categories, so the field is actually ties), youll get an error message and no prop-
named CategoryNa, the 10-character name stored erties are displayed for that field. You must
in the DBF header. either remove the Picture field from the table
RelationsUpgradeResults contains log infor- or change it from General to something else,
mation for upsized relations. This table contains such as Blob.
no errors. However, ViewUpgradeResults, which VFP field-related properties ADS supports are
contains log information for upsized views, has default value, whether nulls are allowed, de-
lots of errors. In fact, all but five views could not scription (which is upsized from the field
be upsized. Some of the views failed because they comment property), field validation message,
reference Categories.CategoryName, which and whether codepage translation is per-
doesnt exist, but there are several different rea- formed, so those are all upsized. ADS doesnt
sons why others failed. support field validation rules, but it does sup-
In VFP, SET DELETED OFF and open Em- port minimum and maximum values, so you
ployees. Note that one of the records, Andrew could manually populate those properties af-
Fuller, is deleted. If you open the FAIL_EMPLO- ter upsizing is complete.
YEES table created by upsizing, youll find An- Other field properties ADS doesnt support
drews record there. Why did upsizing the data- are format, inputmask, field mapping, and
base delete that record? Youll see what happened field captions, so none of these are upsized.
in a moment. All of these are UI-related properties so they
arent necessary in ADS.
While upsizing a VFP database takes you a
long way to having a complete ADS version of the VFP table-related properties ADS supports
database, there are a few things to note. are memo block size, table description (up-
sized from the table comment property), table
ADS doesnt understand nested JOINs, such validation rule, and validation message so
as SELECT * FROM Table1 JOIN Table2 JOIN those are all upsized. However, rules contain-
Table3 ON Table2.Field = Table3.Field ON ing VFP functions are obviously a problem.
Table1.Field = Table2.Field. Youll need to
Triggers arent upsized since they use VFP
convert views that use nested JOINs to the
code which wouldnt be understood by ADS.
more normal sequential JOIN syntax before
However, the most common use for triggers is
upsizing them. One way to do that is to open
to support referential integrity rules and they
the view in the View Designer and save it
are upsized. Note that ADS doesnt support
again. This works because while in older ver-
an Ignore RI rule, so those are upsized as Set
sions of VFP the View Designer used nested
to NULL. Youll have to recreate triggers used
syntax, starting in VFP 8 it uses sequential
for other purposes.
syntax by default.
Stored procedures arent upsized for the same
ADS doesnt support views with ORDER BY
reason. You dont have to worry about stored
clauses.
procedures used for RI since RI rules are up-
Views using VFP functions wont upsize sized. However, youll have to rewrite any
properly. For example, the other stored procedures in ADS SQL or per-
Sales_Totals_By_Amount view in the North- haps move the code into a middle tier com-
wind database uses the VFP BETWEEN() ponent.
function for one of the WHERE conditions. In
Referential integrity is enforced during upsiz-
that case, changing it to use the SQL BE-
ing. Thats why the Andrew Fuller record in
TWEEN clause instead resolves the problem.
Employees was deleted. The Employees table
Other views may not be as easy to fix, how-
has a ReportsTo column that contains the
ever. For example, the Sum-
EmployeeID of each persons manager and
mary_of_Sales_by_Year and Sum-
Andrews ReportsTo value is 0, which doesnt
mary_of_Sales_by_Quarter views both use
match the EmployeeID of any record. The In-
EMPTY() and NVL() in WHERE clauses, so
sert referential integrity rule for this self-join
they cant be upsized and must be recreated
is set to Ignore, so VFP doesnt raise an error
manually.
with this record. However, ADS doesnt have
Tables with General fields wont upsize prop- an Insert referential integrity rule, just Update
erly because ADS doesnt support them (yet and Delete, both of which are set to Restrict.
another reason not to use General fields). Since Andrews ReportsTo value is invalid,
Thats why the Categories table had an error: the RI rule fails and that record is deleted.
Categories.Picture is a General field. In fact, if You could argue that deleting the record is a
you try to display the structure of the table in

Page 7 FoxRockX April 2008


bit harsh; perhaps ADS could set ReportsTo rors. Open the Northwind connection in ARC and
to NULL instead. Interestingly, undeleting it check the log tables. Categories was upsized
in VFP works. properly so now all errors are in views:
ADS version 9 doesnt support VFP binary Sales_by_Category cant be upsized because it
indexes, but Sybase plans to support them in queries on another view, which isnt sup-
version 9.1 or possibly a service pack released ported in ADS.
before 9.1. Ten_Most_Expensive_Products has a TOP
clause and so requires an ORDER BY clause,
You can fix some of these issues and upsize
which isnt supported in ADS.
again. Because ADSUpsize.PRG creates an ADS
Summary_of_Sales_by_Year and Sum-
database, that database cant be open in ARC, so
mary_of_Sales_by_Quarter both use EMPTY()
close the Northwind connection by right-clicking
and NVL() in WHERE clauses.
the connection and choosing Disconnect. Open
the Northwind database in VFP and make the fol-
lowing changes:
Accessing data in VFP or ADS
Invoices and Product_Sales_For_1997: modify Upsizing a VFP database doesnt mean it cant be
these views, remove the relationships, and accessed through VFP anymore. It simply means
recreate them. These views used nested joins that you now have two ways you can access the
so recreating them in VFP 9 will convert them database: as native VFP tables or through ADS.
to sequential join syntax. (Use the View SQL You can modify the data in the tables using VFP
function in the View Designer to confirm or ADS and the other one sees the changes. This
that.) While you could do the same for includes support for auto-incrementing fields.
Sales_By_Category, as youll see later, this
For example, after upsizing the Northwind
view cant be upsized for other reasons.
database, open it in ARC and double-click the
Quarterly_Orders and Employees table to open it. Click the + button in
Sales_Totals_By_Amount: modify these the toolbar at the bottom of the table window (see
views, save, and close without making any Figure 2) to add a record. Leave EmployeeID
changes. These views use the VFP BE- blank but fill in the rest of the fields; be sure to fill
TWEEN() function for one of the WHERE in a valid value (for example, 2) for the Report-
conditions. Simply saving changes it to use sTo field since the RI rule for the table wont allow
the SQL BETWEEN clause. (Prod- a blank or invalid value. Once youve moved off
uct_Sales_For_1997 and Sales_By_Category that row, notice the EmployeeID is automatically
also use BETWEEN() but they were automati- filled in with the next value.
cally fixed in the previous step when you
Now close the table window and disconnect
saved those views.)
from the database in ARC. Open the database in
Alphabetical_List_of_Products, Cur-
VFP and open the Employees table. Notice the
rent_Product_List, Invoices, and Prod-
new record is there. Add a record and notice the
ucts_By_Category: modify these views and
next available ID number is automatically filled in
remove the ORDER BY clause. Unfortunately,
for EmployeeID. VFP doesnt require a valid Re-
you cant do that with
portsTo value; you can leave it blank or type
Ten_Most_Expensive_Products since it has a
something invalid like 99 without an error be-
TOP clause and requires an ORDER BY
cause the Insert RI rule for the self-join is set to
clause. Youll have to recreate that view
Ignore.
manually. Also, while you could make this
change for Sales_By_Category, Sum- As discussed earlier, being able to access your
mary_of_Sales_by_Quarter, and Sum- tables both directly in VFP and through ADS al-
mary_of_Sales_by_Year, it wont help because lows you to migrate an existing application to a
those views cant be upsized for other rea- client/server model one module at a time. For
sons. example, in an accounting system, you could
modify the Accounts Receivable module to access
Invoices: modify this view, choose View SQL,
the data using ADS and deploy that module once
and change both occurrences of ALLTRIM to
the changes are completed and fully tested. The
TRIM, since ALLTRIM() isnt a supported
other modules would continue to access the tables
function in ADS but TRIM() is.
directly in VFP. The benefit of this approach is
Categories: modify this table and remove the
that you dont have to migrate the entire applica-
Picture field.
tion at once and face the much larger develop-
CLOSE TABLES ALL and run ADSUp- ment and testing burden that accompanies such a
size.PRG again. This time you get only four er- wholesale change.

April 2008 FoxRockX Page 8


If you are starting a new application and have and click Next. Specify the desired name for
no requirement for backward compatibility of the the database, select the folder containing De-
data, you might consider using ADS native tables moMemo.DBF, choose vfp for TableType,
(ADT files) rather than DBF files. ADT files have and click OK. (Note that this accesses De-
many advantages over DBFs, including no memo moMemo as a free table rather than through
file bloat, more data types (such as case- an ADS data dictionary because in the beta
insensitive character fields), longer field names, version, theres a problem accessing FTS in-
larger file sizes, more than 255 fields in a table, dexes on DBF files through a data dictionary.)
and automatic reuse of deleted records. Right-click the DemoMemo table under the
Tables node and choose Properties. Select the
Full Text Searching Full Text Index Definitions page (see Figure
ADS has a fast and powerful full text search (FTS) 4), click Add Index, and enter the desired
feature. FTS uses an index on each word in a name of the index. For Key Field, choose
memo field to provide fast, index-based lookups CONTENT (the memo field containing the
for desired words. To enable FTS on a table, you text to index). You can fine-tune the index by
have to create an FTS index on one or more memo specifying the values of other properties, such
fields in the table.
If youd like to test the
performance of FTS but
dont have a large table
with lots of memo content,
a program named MakeDe-
moMemo.PRG included in
the source code for this
article can help. It goes
through your entire hard
drive, looking for text files
(including PRG, TXT, and
HTML) as well as VFP VCX
and SCX files and pulls
them into the Content
memo of a table called De-
moMemo.DBF. As you may
imagine, this can take some
time to run. One test took
about ten minutes to create
an 81,000 record table with
a 545 MB FPT file.
Before adding the table
to ADS, a test program Figure 4. The Full Text Search Index Definitions page of the Table Designer dialog in ARC
looked for all instances of allows you to create indexes that provide fast and powerful full text searching.
the word tableupdate in
Content:
as the minimum and maximum word length,
select * from DemoMemo where ; noise words such as and and the to ex-
atc('table-update', Content) > 0 ; clude from the index, and whether the index
into cursor Temp
select * from DemoMemo ;
is case-sensitive or not. Click OK to create the
where 'tableupdate' $ Content ; index.
into cursor Temp
It can take some time for ADS to create the
The first statement, which uses a case- FTS index since it creates an index entry for every
insensitive search, took 305 seconds. The second, word in every record. Once youve created the
which is case-sensitive but faster, took 65 seconds. index, though, by default its automatically up-
Here are the steps to prepare this table for FTS dated like other indexes are by record additions,
searches: modifications, and deletions. If you wish, you can
Open ARC and choose New Connection Wiz- turn the automatic update off and rebuild the in-
ard from the File menu. Choose Create a dex on demand for better record update perform-
connection to a directory of existing tables ance.

Page 9 FoxRockX April 2008


After creating the FTS index, the following An ODBC connection string is similar:
statement, executed through the ADS engine, took
a mere 0.070 seconds for a case-insensitive search: driver=Advantage StreamlineSQL ODBC;
DataDirectory= C:\ADSTest\Northwind\
Northwind.add;uid=adssys;pwd=""
select * from DemoMemo ;
where contains(Content, 'tableupdate')
If youd rather use an ODBC DSN than a con-
Full text searches have a lot more power than nection string, you can define one using the
just searching for the existence of a word, though. ODBC Data Source Administrator. Open the
For example: Windows Control Panel, open Administrative
Tools, and double-click Data Sources (ODBC).
You can search all fields for a word by speci-
Choose the User DSN tab to create a DSN only
fying * as the field name.
you can use or System DSN to create a DSN any-
You can search for multiple occurrences of the one who logs onto your computer can use. Click
same word in a given record using the Add to create a new DSN, select Advantage
SCORE function. Although not required for StreamlineSQL ODBC for the driver, and click
this function, turning on the Keep Score prop- Finish. Figure 5 shows the ADS ODBC driver
erty for the index gives better performance setup dialog.
since the score value is stored in the index.
Specify a name for the data source and fill in
select * from DemoMemo where ; the path for the ADS data dictionary. Set the op-
contains(Content, 'tableupdate') and;
score(Content, 'tableupdate') > 1
tions as desired (the defaults will do for now) and
click OK.
You can look for one word in proximity to
another:
select * from DemoMemo where ;
contains(Content, ;
'tableupdate near aerror')

One thing to note is that once


youve created an FTS index for a
table, you can no longer access that
table directly in VFP. Trying to do
so causes an operation is invalid
for a Memo, Blog, General or
Picture field error because VFP
doesnt support indexes on these
types of fields.

Connecting to ADS
There are two ways to access data
managed by ADS: using its ODBC
driver or its OLE DB provider.
(There is actually a third way, using
the ADS API functions, but those
are low-level functions requiring a
lot of coding.) OLE DB requires a
connection string while ODBC can
use either an ODBC data source
name (DSN) or a connection string,
sometimes called a DSNless
connection.
Heres an example of an OLE
DB connection string: Figure 5. You can define a DSN using the ADS ODBC driver.

provider=Advantage.OLEDB.1;data source=
C:\ADSTest\Northwind\Northwind.add;
User ID=adssys;Password=""

April 2008 FoxRockX Page 10


else
Accessing ADS using remote views open database Remote
Like a local view, a remote view is simply a pre- endif
defined SQL SELECT statement thats defined in a use Customers
database container. The difference is that a remote
If youre using cursor objects in the DataEnvi-
view accesses data via ODBC rather than natively.
ronment of forms and reports, you have a little bit
You can create a remote view either pro- of extra work to do because those objects have a
grammatically using the CREATE SQL VIEW reference to the specific database you selected
command or visually using the View Designer. In when you dropped the views into the DataEnvi-
both cases, you need to specify an ODBC connec- ronment. To handle this, put code similar to the
tion to use. The connection can either be an ODBC following into the BeforeOpenTables method of
DSN set up on your system or a Connection object the DataEnvironment:
thats already defined in the same database.
Heres an example that defines a Connection ob- local loObject
ject and creates a remote view to the Customers for each loObject in This.Objects
if upper(loObject.BaseClass) = 'CURSOR' ;
table of the upsized Northwind database. This and not empty(loObject.Database)
was excerpted from code generated by GENDBC; loObject.Database = ;
iif(oApp.lUseLocalData, ;
theres actually a lot more code that sets the vari- 'local.dbc', 'remote.dbc')
ous properties of the connection, the view, and endif
the fields. next

CREATE CONNECTION NORTHWINDCONNECTION ; Advantages


CONNSTRING "DSN=Northwind"
CREATE SQL VIEW "CUSTOMERSVIEW" ; The advantages of remote views are:
REMOTE CONNECT "NorthwindConnection" ; You can use the View Designer to create a
AS SELECT * FROM CUSTOMERS Customers
DBSetProp('CUSTOMERSVIEW', 'View', ; remote view visually. Its great to visually see
'UpdateType', 1) all the fields in the underlying tables, easily set up
DBSetProp('CUSTOMERSVIEW', 'View', ; the various parts of the SQL SELECT statement
'WhereType', 3)
DBSetProp('CUSTOMERSVIEW', 'View', ; using a friendly interface, and quickly set proper-
'FetchMemo', .T.) ties of the view using checkboxes or other UI ele-
DBSetProp('CUSTOMERSVIEW', 'View', ; ments.
'SendUpdates', .T.)
DBSetProp('CUSTOMERSVIEW', 'View', ;
From a language point-of-view, remote views
'Tables', 'CUSTOMERS') act just like tables. As a result, they can be
DBSetProp('CUSTOMERSVIEW.customerid', ; used anywhere: you can USE them, add them
'Field', 'KeyField', .T.)
DBSetProp('CUSTOMERSVIEW.customerid', ; to the DataEnvironment of a form or report,
'Field', 'Updatable', .F.) bind them to a grid, process them in a SCAN
DBSetProp('CUSTOMERSVIEW.customerid', ; loop, and so forth.
'Field', 'UpdateName', ;
'CUSTOMERS.CUSTOMERID') Its easier to convert an existing application to
DBSetProp('CUSTOMERSVIEW.companyname', ; use remote views, especially if it already uses
'Field', 'Updatable', .T.)
DBSetProp('CUSTOMERSVIEW.companyname', ;
local views, than using other techniques dis-
'Field', 'UpdateName', ; cussed later.
'CUSTOMERS.COMPANYNAME') Because you can add a remote view to the
DataEnvironment of a form or report, you can
One of the easiest ways you can upsize an ex-
take advantage of the visual support the DE
isting application is using the ADS upsizing util-
provides: dragging and dropping fields or the
ity to create an ADS database from your VFP da-
entire cursor to automatically create controls,
tabase, then create a new VFP database (for ex-
easily binding a control to a field by selecting
ample, REMOTE.DBC) and create remote views in
it from a combobox in the Properties Window,
that database with the same names as the tables
and so on. Also, depending on the settings of
theyre based on. That way, the code to open a
the AutoOpenTables and OpenViews proper-
remote view will be exactly the same as that to
ties, VFP will automatically open the remote
open a local table except youll open a different
views for you.
database first. For example, if you have an appli-
Its easy to update the backend with changes:
cation object named oApp and it has an lUseLo-
assuming the properties of the view have
calData property to indicate whether local or re-
been set up properly, you simply call TA-
mote data is used, this code will open the appro-
BLEUPDATE(). Transaction processing and
priate database and then open either the Custom-
update conflict detection are built-in.
ers table or the Customers remote view:
Remote views are easy to use in a develop-
if oApp.lUseLocalData ment environment: just USE and then
open database Local BROWSE.

Page 11 FoxRockX April 2008


Disadvantages nection string when you open a remote view
The disadvantages of remote views are: with the USE command, meaning that you
Remote views live in a DBC, so thats one can dynamically assemble the database path,
more set of files you have to maintain and in- user name, and password, likely from en-
stall on the clients system. crypted information, just before opening the
view.
Since a remote views SQL SELECT statement
is pre-defined, you cant change it on the fly. Basically, it comes down to a control issue:
Although this is fine for a typical data entry remote views make it easy to work with backend
form, it can be an issue for queries and re- data, but at the expense of limiting the control you
ports. You may have to create several views have over them.
from the same set of data, each varying in the
fields selected, structure of the WHERE Accessing ADS using SQL
clause, and so forth.
passthrough
You cant call a stored procedure from a re- VFP provides a number of functions, sometimes
mote view, so a remote view needs direct ac- referred to as SQL passthrough (or SPT) functions,
cess to the underlying tables. which allow you to access a backend database.
When you use TABLEUPDATE() to write SQLCONNECT() and SQLSTRINGCONNECT()
changes in the view to the backend database, make a connection to the backend database en-
you have little ability (other than by setting a gine. The difference between these two functions
few properties) to control how VFP does the is that SQLCONNECT() requires an existing
update. ODBC DSN while SQLSTRINGCONNECT() uses
As is the case with local views, if you use a a connection string. SQLDISCONNECT() discon-
SELECT * view to retrieve all the fields from a nects from the backend. SQLEXEC() sends a
specific table and the structure of that table on command, such as a SQL SELECT statement, to
the backend changes, the view is invalid and the database engine, typically (but not necessarily,
must be recreated. depending on the command) putting the returned
When you open a view, VFP attempts to lock results into a VFP cursor.
the views records in the DBC, even if only Heres an example that connects to the up-
briefly. This can cause contention in busy ap- sized Northwind database, retrieves all custom-
plications where several users might try to ers, and disconnects. (This assumes theres a DSN
open a form at the same time. Although there called Northwind that defines how to connect
are workarounds (copying the DBC to the lo- to this database.)
cal workstation and using that one or, in VFP
7 and later, using SET REPROCESS SYSTEM lnHandle = sqlconnect('Northwind')
to increase the timeout for lock contention), sqlexec(lnHandle, 'select * from Customers')
browse
its something you must plan for. sqldisconnect(lnHandle)
Until VFP 8, which allows you to specify the
connection handle to use when you open a To use a DSNless connection instead, replace
remote view with the USE statement, you had the SQLCONNECT() statement with the follow-
little ability to manage the connections used ing:
by your application.
lcConnString = 'driver=Advantage '+ ;
The connection information used for a remote 'StreamlineSQL ODBC;'DataDiretory='+ ;
view is hard-coded in plain text in the DBC. 'C:\ADSTest\Northwind\Northwind.add;'
lnHandle = sqlstringconnect(lcConnString)
That means that a hacker can easily discover
the keys to your backend kingdom (such as Table 1 lists the keywords you can specify in
the user name and password) using nothing a connection string; most of these have equiva-
more complicated than Notepad to open the lents in the ODBC dialog shown in Figure 5. All
DBC. This isnt much of an issue starting in but DataDirectory are optional.
VFP 7 because it allows you to specify a con-

DOWNLOADS
Subscribers can download FR200804_code.zip in the SourceCode sub directory of the document portal. It contains the following files:
doughennig200804_code.zip
Source code for the article "Advantage Database Server for Visual FoxPro Developers" from Doug Hennig

April 2008 FoxRockX Page 12


Table 1. ADS ODBC driver connection string keywords.
Keyword Description
DataDirectory Specify the path and name of the ADD file to use an ADS database. For free tables, specify the
directory for the tables.
DefaultType For free tables, specify FoxPro for VFP tables or Advantage for ADS tables (ADT files). This
setting is ignored for databases.
ServerTypes Specify a numeric value thats the sum of the types of ADS server to connect to: Remote (2),
Local (1), or Internet (4). For example, use 3 (2 + 1) for Remote and Local.
AdvantageLocking ON (the default) to use ADS proprietary locking or OFF for VFP-compatible locking.
Locking Record (the default) for record locking or File to lock the entire file during updates.
Rows Similar to SET DELETED. True displays deleted records while False (the default) omits
them. This setting is ignored for ADS tables because deleted records are never visible in that
case.
TrimTrailingSpaces Similar to Varchar fields. True removes trailing spaces from character fields returned to the
application and False (the default) does not.
MemoBlockSize Similar to the SET BLOCKSIZE command. Specify the block size for memo fields for new ta-
bles. The default is 64 for VFP tables and 8 for ADS tables.
CharSet The collation setting to use: ANSI (the default) or OEM. If you use OEM, you must also
specify the Language setting.
Language The language to use if CharSet=OEM.
MaxTableCloseCache The number of cursors in the ADS cache; the default is 5.
Compression The type of compression to use. See the ADS help file for a discussion of the types of compres-
sion available.
CommType The communication protocol to use. See the ADS help file for details.

Regardless of whether you use a DSN or a for Date and {ts 'YYYY-MM-DD HH:MM:SS'} for
connection string to connect to ADS, you then use DateTime. Heres an example:
the same type of SQL statements youd use to ac-
cess, update or delete records in VFP tables, but sqlexec(lnHandle, "select * from orders " + ;
"where OrderDate between " + ;
you use the SQLEXEC() function to execute them. "{d '1997-07-01'} and {d '1997-07-31'}")
In addition to DML (Data Manipulation Lan-
guage) functions like SELECT, INSERT, UPDATE, Heres a function called VFP2ODBCDate that
and DELETE, the ODBC driver also supports converts VFP Date and DateTime values to ODBC
DDL (Data Definition Language) functions such syntax:
as CREATE DATABASE | TABLE | INDEX |
lparameters tuDate
VIEW | PROCEDURE, DROP INDEX | TABLE | local lcDate, ;
VIEW | PROCEDURE, and ALTER TABLE. lcReturn
lcDate = transform(year(tuDate)) + ;
Note that while ADS supports most of the '-' + padl(month(tuDate), 2, '0') + ;
VFP data types, how you specify values for Logi- '-' + padl(day(tuDate), 2, '0')
cal, Date, and DateTime fields is a little different if vartype(tuDate) = 'D'
lcReturn = "{d '" + lcDate + "'}"
than with VFP syntax. ADS Logical values come else
back to VFP as Logical fields but you must specify lcReturn = "{t '" + lcDate + ;
' ' + padl(hour(tuDate), 2, '0') + ;
them using True or 1 for true and False or 0 for ':' + padl(minute(tuDate), 2, '0') + ;
false. For example, only the last two of the follow- ':' + padl(sec(tuDate), 2, '0') + "'}"
ing statements succeeds: endif vartype(tuDate) = 'D'
return lcReturn
sqlexec(lnHandle, ;
"select * from Products where Discontinued") The following example uses this function:
sqlexec(lnHandle, "select * from " + ;
"Products where Discontinued=.T.") ldFrom = {^1997-07-01}
sqlexec(lnHandle, "select * from " + ; ldTo = {^1997-07-31}
"Products where Discontinued=True") sqlexec(lnHandle, "select * from orders " + ;
sqlexec(lnHandle, "select * from " + ; "where OrderDate between " + ;
"Products where Discontinued=1") VFP2ODBCDate(ldFrom) + " and " + ;
VFP2ODBCDate(ldTo))
(Sybase iAnywhere has indicated they may
support .T. and .F. syntax in the release ver- Instead of converting VFP values into ODBC
sion.) syntax, you can use a parameterized query, re-
placing a hard-coded value with a variable name
Date and DateTime values must be specified
prefixed with ? (the variable, of course, must be
using standard ODBC syntax: {d 'YYYY-MM-DD'}

Page 13 FoxRockX April 2008


in scope). In that case, VFP will take care of data but call a stored procedure to update the ADS
type conversions for you. This has the additional tables.
benefit of avoiding SQL injection attacks (which You can manage your own connections. For
are beyond the scope of this article). For example: example, you might want to use a connection
manager object to manage all the connections
ldFrom = {^1997-07-01}
ldTo = {^1997-07-31}
used by your application in one place.
sqlexec(lnHandle, 'select * from orders ' + ;
'where OrderDate between ?ldFrom and ?ldTo')
Disadvantages
Although VFP can use single quotes, double The disadvantages of using SPT are:
quotes, and square brackets as string delimiters,
pass string values to SPT functions delimited with Its more work, since you have to code every-
single quotes only. thing: creating and closing the connection, the
SQL SELECT statements to execute, and so
Although these examples omit it, be sure to
on. You dont have a nice visual tool like the
check the return value of SQLEXEC(). If it returns
View Designer to show you which fields exist
something less than 1, the command failed so use
in which tables.
AERROR() to determine what went wrong. Also,
You cant visually add a cursor created by
you can specify the name of the cursor to create as
SPT to the DataEnvironment of a form or re-
the third parameter to SQLEXEC(); the cursor is
port. Instead, you have to code the opening of
named SQLResult if you omit this parameter.
the cursors (for example, in the Before-
OpenTables method), you have to manually
Advantages create the controls, and you have to fill in the
The advantages of using SPT are:
binding properties (such as ControlSource) by
You have a lot more flexibility in data access typing them yourself. Dont make a typo
than with remote views, such as calling stored when you enter the alias and field names or
procedures using the SQLEXEC() function. the form wont work.
You can change the connection information Theyre harder to use than remote views in a
on the fly as needed. For example, you can development environment: instead of just is-
store the user name and password as en- suing a USE command, you have to create a
crypted values and only decrypt them just be- connection, then use a SQLEXEC() call to get
fore using them in the SQLCONNECT() or the data you want to look at. You can make
SQLSTRINGCONNECT() functions. As men- things easier on yourself if you create a set of
tioned earlier, this isnt nearly the advantage PRGs to do the work for you or you can use
over remote views that it used to be, since the Data Explorer that comes with VFP to ex-
VFP 7 and later allows you to specify the con- amine the structures and contents of the ta-
nection string on the USE command. bles. You can even create a DBC and set of
You can change the SQL SELECT statement as remote views used only in the development
needed. For example, you can easily vary the environment as a quick way to look at the
list of the fields, the WHERE clause (such as data.
changing which fields are involved or elimi- Cursors created with SPT can be updatable,
nating it altogether), the tables, and so on. but you have to make them so yourself using
You dont need a DBC to use SPT, so theres a series of CURSORSETPROP() calls to set the
nothing to maintain or install, lock contention SendUpdates, Tables, KeyFieldList, Up-
isnt an issue, and you dont have to worry datableFieldList, and UpdateNameList prop-
about a SELECT * statement being made inva- erties. Also, you have to manage transaction
lid when the structure of the backend tables processing and update conflict detection
change. yourself.
As with remote views, the result set of a SPT Since SPT cursors arent defined like remote
call is a VFP cursor, which can be used any- views, you cant easily switch between local
where in VFP. and remote data using SPT as you can with
Although you have to code for it yourself remote views by simply changing which view
(this is discussed in more detail under Disad- you open in a form or report.
vantages), you have greater control over how
updates are done. For example, you might use
a SQL SELECT statement to create the cursor

April 2008 FoxRockX Page 14


Accessing ADS using ADO Notice how this code uses object-oriented
OLE DB providers are similar to ODBC drivers: code to access the Recordset. The EOF property is
they provide a standard, consistent way to access the equivalent of the VFP EOF() function and the
data sources. Because OLE DB is a set of low-level MoveNext method is like SKIP. To access the
COM interfaces, its not easy to work with in lan- value of a field in the current record, use Record-
guages like VFP. To overcome this, Microsoft cre- set.Fields('FieldName').Value.
ated ActiveX Data Objects (ADO), a set of COM Using parameterized queries with ADO is a
objects that provide an object-oriented front-end little more work than it is with ODBC. In addition
to OLE DB. to specifying a parameter as ? (without the
ADO consists of several objects, including: variable name), you also have to use ADO Com-
Connection: This is the object responsible for mand and Parameter objects to specify the pa-
communicating with the data source. rameter and its value.
Recordset: This is the equivalent of a VFP cur- * Connect to the ADS database.
sor: it has a defined structure, contains the
data in the data set, and provides properties loConn = createobject('ADODB.Connection')
loConn.ConnectionString = ;
and methods to add, remove, or update re- 'provider=Advantage.OLEDB.1;' + ;
cords, move from one to another, filter or sort 'data source=' + ;
the data, and update the data source. 'c:\adstest\northwind\northwind.add'
loConn.Open()
Command: This object provides the means of
doing more advanced queries than a simple * Create a Command object and define the
* command type and connection.
SELECT statement, such as parameterized
queries and calling stored procedures. loCommand = createobject('ADODB.Command')
loCommand.CommandType = adCmdText
Heres an example (ADOExample.PRG) that loCommand.ActiveConnection = loConn

gets all Brazilian customers from the upsized * Create a Parameter object, set its
Northwind database and displays the customer * properties, and add it to the Command
* object.
ID and company name. Notice that the Connec-
tion object handles the connection while the Re- loParameter = loCommand.CreateParameter;
cordset handles the data. This code references ('Country', adChar, adParamInput, 15)
loParameter.Value = 'UK'
ADOVFP.H, an include file of constants useful loCommand.Parameters.Append(loParameter)
when working with ADO.
* Execute a parameterized query and
#include ADOVFP.H * display the results.
local loConn as ADODB.Connection, ;
loRS as ADODB.Recordset, ; loCommand.CommandText = 'select * from ' + ;
lcCustomers 'customers where country = ?'
loRS = loCommand.Execute()
* Connect to the ADS database. * same code as above to display the results

loConn = createobject('ADODB.Connection')
loConn.ConnectionString = ;
'provider=Advantage.OLEDB.1;' + ; Advantages
'data source=' + ; The advantages of using ADO are:
'c:\adstest\northwind\northwind.add'
loConn.Open() Many of the advantages are the same as with
SPT: you have more flexibility in data access
* Create a Recordset and set its properties.
than with remote views, you can change the
loRS = createobject('ADODB.Recordset') connection information on the fly as needed,
loRS.ActiveConnection = loConn you can change the SQL SELECT statement as
loRS.LockType = 3 && adLockOptimistic
loRS.CursorLocation = 3 && adUseClient needed, you can manage your own connec-
loRS.CursorType = 3 && adOpenStatic tions, and theres no DBC involved.
* Execute a query and display the results.
Although performance differences arent sig-
nificant in simple scenarios (in fact, in general,
loRS.Open("select * from customers " + ; ODBC is faster than ADO), ADO is more scal-
"where country='Brazil'")
lcCustomers = '' able in heavily-used applications such as Web
do while not loRS.EOF servers.
lcCustomers = lcCustomers + ;
loRS.Fields('customerid').Value +chr(9)+;
ADO is object-oriented, so you can deal with
loRS.Fields('companyname').Value + chr(13) the data like objects.
loRS.MoveNext() Depending on how theyre set up, ADO Re-
enddo while not loRS.EOF
messagebox(lcCustomers) cordsets are automatically updateable without
loRS.Close() any additional work other than calling the
loConn.Close()

Page 15 FoxRockX April 2008


Update or UpdateBatch methods. Transaction existing application from one mechanism to an-
processing and update conflict detection are other is a non-trivial task.
built-in. Fortunately, theres a VFP technology that
You can easily persist a Recordset to a local provides a common interface for both ODBC and
file, then later reload it and carry on working, OLE DB: the CursorAdapter class. Cursor-
and finally update the ADS data source. This Adapter, added in VFP 8, is a great solution be-
makes it a much better choice for road war- cause:
rior applications than remote views or SPT. It makes it easy to use ODBC, ADO, or XML,
even if youre not very familiar with these
technologies.
Disadvantages
The disadvantages of ADO are: It provides a consistent interface to remote
data regardless of the mechanism you choose.
Its more work, since you have to code every-
It makes it easy to switch from one mecha-
thing: creating and closing the connection, the
nism to another.
SQL SELECT statements to execute, and so
on. You dont have a nice visual tool like the Heres an example of the last point. Suppose
View Designer to show you which fields exist you have an application that uses ODBC with
in which tables on the backend. CursorAdapters to access ADS data, and for some
An ADO Recordset is not a VFP cursor, so reason you want to change to use ADO instead.
you cant use it in places that require a cursor, All you need to do is change the DataSourceType
such as grids and reports. There are functions of the CursorAdapters and change the connection
in the VFPCOM utility (available for to the ADS database, and youre done. The rest of
download from the VFP home page, the components in the application neither know
http://msdn.microsoft.com/vfoxpro) that nor care about this; they still see the same cursor
can convert a Recordset to a cursor and vice regardless of the mechanism used to access the
versa, but using them can impact perform- data.
ance, especially with large data sets, and they Heres an example (CursorAdapterExam-
have known issues with certain data types. If ple.PRG) that gets certain fields for Brazilian cus-
you want to use ADO, CursorAdapter (dis- tomers from the Customers table in the North-
cussed next) is the way to go. wind database. The cursor is updateable, so if you
Theres no visual support for ADO Record- make changes in the browse window, close it, and
sets, so you have to code their creation and then run the program again, youll see that your
opening, you have to manually create the con- changes were saved.
trols, and you have to fill in the binding prop-
erties (such as ControlSource) by typing them local lcConnString, ;
yourself. This is even more work than for SPT, lnHandle, ;
loCursor as CursorAdapter, ;
because the syntax isnt just CUR- laErrors[1]
SOR.FIELDits Record- close tables all
set.Fields('FieldName').Value. * Connect to ADS.
Theyre the hardest of the technologies to use
in a development environment, since you lcConnString = 'driver=' + ;
'Advantage StreamlineSQL ODBC;' + ;
have to code everything: making a connec- 'DataDirectory=C:\ADSTest\Northwind\' + ;
tion, retrieving the data, and moving back 'Northwind.add;'
lnHandle = sqlstringconnect(lcConnString)
and forth between records. You cant even
BROWSE to see visually what the result set * Create a CursorAdapter and set its
looks like (unless you use VFPCOM or Cur- * properties.
sorAdapter to convert the Recordset to a cur- loCursor = createobject('CursorAdapter')
sor). with loCursor
.Alias = 'Customers'
Theres a bigger learning curve involved with .DataSourceType = 'ODBC'
ADO than using the cursors created by .DataSource = lnHandle
ODBC. .SelectCmd = "select " + ;
"CUSTOMERID, COMPANYNAME, CONTACTNAME "+;
"from CUSTOMERS where COUNTRY = 'Brazil'"
.KeyFieldList = 'CUSTOMERID'
Accessing ADS using CursorAdapter .Tables = 'CUSTOMERS'
.UpdatableFieldList = 'CUSTOMERID, ' + ;
One of the things youve likely noted is that each 'COMPANYNAME, CONTACTNAME'
of the mechanisms discussed is totally different .UpdateNameList = 'CUSTOMERID ' + ;
from the others. That means you have a new 'CUSTOMERS.CUSTOMERID, ' + ;
'COMPANYNAME CUSTOMERS.COMPANYNAME, ' + ;
learning curve with each one, and converting an

April 2008 FoxRockX Page 16


'CONTACTNAME CUSTOMERS.CONTACTNAME' DataSourceType: specifies how to access the
if .CursorFill()
browse data. The choices are Native, XML,
else ODBC, and ADO, although only the lat-
aerror(laErrors)
messagebox(laErrors[2])
ter two are used with ADS.
endif .CursorFill() DataSource: the value of this property de-
endwith pends on what DataSourceType is set to. In
the case of ODBC, it must be an open ODBC
You dont have to create a CursorAdapter
connection handle. For ADO, its an ADO Re-
programmatically. You can use the Class Designer
cordset object with its ActiveConnection set to
to create a CursorAdapter subclass and either fill
an open Connection object. Note that in either
in the properties in the Properties window or use
case, youre responsible for opening and man-
the CursorAdapter Builder, which provides a nice
aging the connection yourself.
visual tool for the CursorAdapter (see Figure 6).
Note that the CursorAdapter Builder doesnt SelectCmd: the SQL statement to execute to
quite work right with ADS when youre using retrieve the data.
ODBC to connect to the database; see the Fixing Alias: the alias of the cursor created.
the VFP CursorAdapter Builder sidebar for de- KeyFieldList, Tables, UpdatableFieldList, and
tails and how to correct the problem. UpdateNameList: these fields are the key to

Figure 6. The CursorAdapter Builder provides a visual tool to create CursorAdapter subclasses.

Although CursorAdapter has quite a few making the cursor updateable. Set them to the
properties and methods, the most important ones appropriate values and CursorAdapter will
are: automatically write changes back to the

Page 17 FoxRockX April 2008


source data. See the VFP documentation for tnOptions, ;
toSource
details on these properties. local loSource as ADODB.Command, ;
CursorFill: call this method to create the cur- lnOptions, ;
llUseCursorSchema, ;
sor and execute the statement in SelectCmd to llNoData, ;
populate the cursor. llReturn, ;
laError[1]
CursorDetach: by default, the cursor created
by CursorAdapter is attached to the Cur- * If we have a parameterized query, we need
sorAdapter object. When the CursorAdapter * an ADO Command object. Create one
* if it wasn't passed.
is destroyed (such as when it goes out of
scope), the cursor is automatically closed. If if '?' $ This.SelectCmd and ;
you want the cursor to remain open, call the vartype(toSource) <> 'O'
loSource = createobject('ADODB.Command')
CursorDetach method to detach the cursor loSource.ActiveConnection = ;
from the CursorAdapter. This.DataSource.ActiveConnection
lnOptions = adCmdText
else
A CursorAdapter can use either ODBC or loSource = toSource
ADO to connect to ADS. For ODBC, open a con- lnOptions = tnOptions
nection to the database using SQLCONNECT() or endif '?' $ This.SelectCmd ...
SQLSTRINGCONNECT() and set the Cur- * If the first two parameters weren't
sorAdapter DataSource property to the connec- * specified, we don't want to explicitly
tion handle and set the DataSourceType property * pass .F., so use the default values.
* If CursorSchema is empty, we'll, of
to ODBC. ADO is a little more work: instantiate * course, pass .F. for the first parameter.
and open an ADO Connection object, instantiate a
do case
Recordset object, set the Recordsets ActiveCon- case pcount() >= 2
nection property to the Connection object, set the llUseCursorSchema = tlUseCursorSchema
CursorAdapters DataSource property to the Re- llNoData = tlNoData
case pcount() = 1
cordset object, and set DataSourceType to ADO. llUseCursorSchema = tlUseCursorSchema
For parameterized queries, use the ?Variable- llNoData = This.NoData
Name syntax in your SQL statement, even for case pcount() = 0
llUseCursorSchema = This.UseCursorSchema
ADO. For ADO, though, you must also instantiate llNoData = This.NoData
an ADO Command object and pass it as the endcase
if empty(This.CursorSchema)
fourth parameter to CursorFill (dont worry about llUseCursorSchema = .F.
the Parameter object; VFP takes care of that inter- endif empty(This.CursorSchema)
nally). llReturn = dodefault(llUseCursorSchema, ;
llNoData, lnOptions, loSource) and ;
Instead of doing all that work for ADO used(This.Alias)
manually, the SFCursorAdapterADO subclass
* If something went wrong, find out why.
included in SFCursorAdapter.VCX in the source
code for this article does some of this work for if not llReturn
you. Its DataSourceType property is set to ADO aerror(laError)
This.cErrorMessage = laError[2]
and its Init method sets up the DataSource prop- endif not llReturn
erty. return llReturn

local loRS as ADODB.Recordset Heres an example that uses SFCursorAdapter-


loRS = createobject('ADODB.RecordSet') ADO, taken from ADOCursorAdapterExample.PRG:
loRS.CursorLocation = 3 && adUseClient
loRS.LockType = 3 && adLockOptimistic local loConnection as ADODB.Connection, ;
This.DataSource = loRS loCA as SFCursorAdapterADO ;
of SFCursorAdapter.vcx
After creating and opening a Connection ob- private pcCountry
ject, pass it to SetConnection.
* Create and open an ADO Connection object.

lparameters toConnection loConnection = createobject(;


This.DataSource.ActiveConnection = ; 'ADODB.Connection')
toConnection loConnection.ConnectionString = ;
'Provider=Advantage.OLEDB.1;' + ;
You dont have to pass a Command object to 'Data Source=C:\ADSTest\northwind\' + ;
'northwind.add;' + ;
CursorFill; SFCursorAdapterADO automatically 'User ID=adssys;Password=""'
uses a Command object if theres a ? in the SQL loConnection.Open()
statement.
* Create an SFCursorAdapterADO object
* and set it up.
lparameters tlUseCursorSchema, ;
tlNoData, ; loCA = newobject('SFCursorAdapterADO', ;

April 2008 FoxRockX Page 18


'SFCursorAdapter.vcx') SELECT statement as needed, you dont need
with loCA
.SetConnection(loConnection) a DBC, and you can manage your own con-
.SelectCmd = 'select * from customers ' + ; nections.
'where country = ?pcCountry'
.Alias = 'customers' Although you have to code for it yourself,
you have greater control over how updates
* Do the query and either are done. For example, you might use a SQL
* show the result set or an error.
SELECT statement to create the cursor but call
pcCountry = 'Germany' a stored procedure to update the backend ta-
if .CursorFill()
browse
bles.
else
messagebox(loCa.cErrorMessage)
endif .CursorFill()
endwith
Disadvantages
There arent a lot of disadvantages for Cur-
* Close the connection. sorAdapters:
loConnection.Close() You cant use a nice visual tool like the View
Designer to create CursorAdapters, although
the CursorAdapter Builder is a close second.
Advantages Like all new technologies, theres a learning
The advantages of CursorAdapters are essentially curve that must be mastered.
the combination of those of all of the other tech-
nologies.
Depending on how its set up (if its com- Licensing
pletely self-contained, for example), opening a Advantage Database Server uses a concurrent li-
cursor from a CursorAdapter subclass can censing model; you need one license per con-
almost be as easy as opening a remote view: nected user. Each workstation can have an unlim-
you simply instantiate the subclass and call ited number of database connections. Multiple
the CursorFill method. You could even call Advantage-enabled applications running on a
that from Init to make it a single-step opera- single workstation are licensed as a single user.
tion. Sybase iAnywhere does not publish a price list for
Its easier to convert an existing application to licenses. Although they state that they have flexi-
use CursorAdapters than to use cursors cre- ble pricing options and OEM partner discounts
ated with SPT. and that their price is competitive to other data-
Like remote views, you can add a Cur- base servers, you must contact them for a quote
sorAdapter to the DataEnvironment of a form based on how many licenses you require.
or report and take advantage of the visual
support the DE provides: dragging and drop- Resources
ping fields to automatically create controls, The Advantage Developer Zone site,
easily binding a control to a field by selecting http://devzone.advantagedatabase.com, has nu-
it from a combobox in the Properties Window, merous resources for learning more about ADS,
and so on. such as newsgroups (including a VFP-specific
Its easy to update the backend with changes: newsgroup), online documentation, white papers,
assuming the properties of the view have tutorials, and sample code. Also, a book by Cary
been set up properly, you simply call TA- Jensen and Loy Anderson, Advantage Database
BLEUPDATE(). Server: A Developers Guide (ISBN 978-1-4259-7726-
Because the result set created by a Cur- 9), provides a great introduction to ADS. Al-
sorAdapter is a VFP cursor, they can be used though none of the examples are in VFP, VFP de-
anywhere in VFP: in a grid, a report, proc- velopers will have no trouble understanding and
essed in a SCAN loop, and so forth. This is translating the code.
true even if the data source comes from ADO Andrew MacNeill interviewed J.D. Mullin, R
and XML, because the CursorAdapter auto- & D Manager for ADS, in The FoxShow #49, a
matically takes care of conversion to and from podcast available for download at
a cursor for you. http://akselsoft.libsyn.com/index.php?post_id=3
You have a lot of flexibility in data access, 02994. This interview provides some background
such as calling stored procedures or middle- to ADS and VFP and discusses some of the design
tier objects. features of ADS.
You can change the connection information
on the fly as needed, you can change the SQL

Page 19 FoxRockX April 2008


Summary Fixing the VFP
Advantage Database Server is an exciting data-
base engine that provides better support for VFP CursorAdapter Builder
application developers than any other cli- The CursorAdapter Builder has a problem with
ent/server database engine. It can form the basis the ADS ODBC driver (it works fine with the ADS
of a migration strategy to move your applications OLE DB provider). The Select Command Builder
from file-based data access to true client/server dialog displays when you click the Build button
technology. for the Select command in page 2 of the Cur-
At the time this article was written, ADS ver- sorAdapter Builder. This dialog gives an error
sion 9 was in beta, so its possible there are with the ADS ODBC driver because the driver
changes in the final release of the product from returns a different result set to the SQLTABLES()
what is discussed in this article. However, the and SQLCOLUMNS() functions than SQL Server
basic concepts remain the same, including the and many other drivers do. Rather than having
benefits of ADS and how VFP applications access fields named TABLE_NAME and COL-
the database engine. UMN_NAME, ADS names them TABLENAME
and COLUMNNAME.
Fortunately, Microsoft provides the source code
Fixing the ADS for the CursorAdapter Builder and the fix was
VFP upsizing utility easy, so the source code accompanying this article
Although this will change when the production includes a replacement DEBuilder.APP that takes
version is released, the beta version of the ADS care of these issues. Copy that file to the Wizards
VFP Upsizing Utility, ADSUpsize.PRG, which folder of the VFP home directory, overwriting the
came with the beta version had a few issues: existing file. The source code for the fix is in the
It didnt handle auto-incrementing fields included DECABuilder.VCX, which you normally
properly. find in the Tools\XSource\VFPSource\Wi-
zards\DEBuilder folder of the VFP home direc-
It gave an error in the AddDatabaseToTable
tory after extracting XSource.ZIP in
method for some tables.
Tools\XSource.
It didnt allow you to run it more than once
without doing some manual cleanup tasks.
Biography
It didnt properly handle the case where it Doug Hennig is a partner with Stonefield Systems Group Inc.
couldnt connect to the ADS OLE DB pro- and Stonefield Software Inc. He is the author of the award-
vider. winning Stonefield Database Toolkit (SDT); the award-winning
Stonefield Query; the MemberData Editor, Anchor Editor, and
It named relationships between table with CursorAdapter and DataEnvironment builders that come with
unhelpful names like Relation_1 and Rela- Microsoft Visual FoxPro; and the My namespace and updated
Upsizing Wizard in Sedna. Doug is co-author of the Whats
tion_2. New in Visual FoxPro series (the latest being Whats New in
It didnt handle differences between VFP syn- Nine) and The Hackers Guide to Visual FoxPro 7.0. He is
the technical editor of The Hackers Guide to Visual FoxPro
tax and ADS syntax when upsizing views, 6.0 and The Fundamentals. All of these books are from
particularly with filters on Logical, Date, or Hentzenwerke Publishing (www.hentzenwerke.com). Doug
DateTime fields or with references to the da- wrote over 100 articles in 10 years for FoxTalk and has written
numerous articles in FoxPro Advisor and Advisor Guide. He
tabase container (that is, Data- has spoken at every Microsoft FoxPro Developers Conference
baseName!TableName). (DevCon) since 1997 and at user groups and developer con-
It gave an error on tables without indexes. ferences all over the world. He is one of the administrators for
the VFPX VFP community extensions Web site
It didnt properly upsize tables with Varchar (www.codeplex.com/VFPX) and one of the organizers of the
or Varbinary fields. Southwest Fox conference (www.swfox.net). He has been a
Microsoft Most Valuable Professional (MVP) since 1996. Doug
was awarded the 2006 FoxPro Community Lifetime Achieve-
Fortunately, these were all easy to fix, so the ment Award (fox.wikis.com/wc.dll?Wiki~FoxProCom-
source code accompanying this article includes a munityLifetimeAchievementAward~VFP). Web:
replacement ADSUpsize.PRG that takes care of www.stonefield.com and www.stonefieldquery.com, Email:
dhennig@stonefield.com, Blog: doughennig.blogspot.com
these issues.

FoxRockX(ISSN-1866-4563) FoxRockX is published bimonthly by ISYS GmbH


dFPUG c/o ISYS GmbH Copyright
Frankfurter Strasse 21 B 2008 ISYS GmbH. This work is an independently produced publication of ISYS GmbH, Kronberg, the content of which is
61476 Kronberg, Germany the property of ISYS GmbH or its affiliates or third-party licensors and which is protected by copyright law in the U.S. and
Phone +49-6173-950903 elsewhere. The right to copy and publish the content is reserved, even for content made available for free such as sample
Fax +49-6173-950904 articles, tips, and graphics, none of which may be copied in whole or in part or further distributed in any form or medium
Email: foxrockx@dfpug.de without the express written permission of ISYS GmbH. Requests for permission to copy or republish any content may be
Editor: Rainer Becker directed to Rainer Becker.
FoxRockX, FoxTalk 2.0 and Visual Extend are trademarks of ISYS GmbH. All product names or services identified throughout this journal are trademarks or
registered trademarks of their respective companies. Printed in the Czech Republic.

April 2008 FoxRockX Page 20

También podría gustarte