Error handling and debugging in traditional Visual FoxPro file server applications is relatively straightforward because traditional Visual FoxPro applications use a single technology that controls the user interface, data access, and the actual storage of data. Client/server applications use three separate technologies (the Visual FoxPro user interface, ODBC and a SQL Server database), which must communicate with each other. Because this architecture is more complex, the process of handling errors in the applicationand debugging is also more complex. In this chapter, you will learn some of the secrets of handling client/server errors. You are also introduced to some debugging tools that will make debugging easier for you.
Handling errors
After reading this far into the book, you may have gathered that handling data errors in a client/server application is not very different from a traditional Visual FoxPro application. This is simply because most data updates are handled through a TABLEUPDATE() or SQLExec() call,and you can use the AERROR() function to determine the cause of any failures. Any other type of failure (such as an application error) is trapped with either the Error event of the object, or through your global ON ERROR handler.
Unfortunately, it is not that simple: Handling client/server errors from Visual FoxPro can get tricky, particularly when SQL Server is used in a way that is not friendly to the client application.
Trapping errors
The first lesson to learn is how to trap the errors you receive during a TABLEUPDATE() call. You probably know that this function does not report errors through the ON ERROR handler. Instead, you must use the AERROR() function to capture the reasons for TABLEUPDATE() failures. In file-server applications, this array has a single row containing the details of the failure. For any Visual FoxPro error, the array contains the data shown in Table 1.
Table 1. The elements returned by AERROR() for Visual FoxPro errors.
Element
Data type
Contains
Description
1
Numeric
Number
Visual FoxPro error number.
2
Character
String
Visual FoxPro error message.
3
Character
NULL or string
Optional error parameter (same as SYS(2018)).
4
Numeric
NULL or number
Work area where error occurred.
5
Numeric
NULL or number
Trigger that caused the failure (1=insert,
2=update, 3=delete).
6
NULL
Always null.
7
NULL
Always null.
However, for errors that occur through an ODBC update, such as when updating data on a SQL Server with a remote view, the array will always have the same value, 1526, in the first column. This is because all ODBC errors trigger the same Visual FoxPro error (1526). The remaining elements of the array contain data that differs from a traditional Visual FoxPro error, since ODBC errors are reported differently. The contents of the array from an ODBC error are shown in Table 2.
Table 2. The elements returned by AERROR() for an ODBC error.
Element
Data type
Contains
Description
1
Numeric
1526
Error number, always 1526.
2
Character
Error message text.
Visual FoxPro error message.
3
Character
ODBC error
message text
The error parameter—same info as column 2,
but less of it
4
Character
ODBC SQL state
If the error is directly related to ODBC, this is an
error number that describes the error. These codes can be found in the ODBC Programmer’s Reference.
5
Numeric
ODBC data source
error number
If the error is a SQL Server error, this element
contains the SQL Server error number, which can be found in the SQL Server Books Online.
6
Numeric
ODBC connection
handle
Shows the connection handle across which the
error occurred.
7
NULL
Always null.
‡
ODBC errors can create multiple rows in the array created by AERROR(). Normally, you are primarily interested in the data of the first row, but the other rows could contain important information that is related to the error
reported in the first row.
Reporting errors
By analyzing the array, you can quickly determine the cause of any update errors by reading the fifth column and comparing the value there with error numbers from SQL Server. For example, imagine that you are working with the sample pubs database on SQL Server. In this database, there is an authors table, which contains information about the authors of the books in the database. The sample CREATE TABLE statement shown here defines the first column of the authors table, named au_id:
CREATE TABLE authors
(au_id varchar(11) NOT NULL
CHECK (au_id like '[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]'),
--other columns defined here…
From this statement, you can see that the au_id column has a CHECK constraint
(equivalent to a field validation rule in Visual FoxPro), restricting the data to the format
999-99-9999, where 9 represents any valid digit. Therefore, if you attempt to place any data into this column that does not meet this CHECK constraint, the operation fails.
‡
The CHECK constraint created here does not specify a name for the constraint. This causes SQL Server to generate a name for the constraint in the format CK tablename fieldname xxxxxxxx, where xxxxxxxx is a
unique character string. To avoid this, you should always provide a name for all constraints, for reasons that should become clear in the next few paragraphs.
Now imagine that you have created a view that retrieves data from the authors table and allows the user to update that data. Suppose that after your user has pulled down a record to edit, he or she decides to change the au_id column of an author. In doing so, the CHECK constraint on the column is violated, which causes TABLEUPDATE() to fail. After invoking the AERROR() function, the resultant array will contain the following information:
LAERROR
Pub
A
( 1,
1)
N
1526 ( 1526.00000000)
( 1,
2)
C
"Connectivity error: [Microsoft][ODBC SQL Server
Driver][SQL Server]UPDATE statement conflicted with
COLUMN CHECK constraint 'CK authors au_id08EA5793'.
The conflict occurred in database 'pubs', table
'authors', column 'au_id'."
( 1,
3)
C
"[Microsoft][ODBC SQL Server Driver][SQLServer]UPDATE
statement conflicted with COLUMN CHECK constraint
'CK authors au_id 08EA5793'. The conflictoccurred in
database 'pubs', table 'authors', column 'au_id'."
( 1,
4)
C
"23000"
( 1,
5)
N
547 ( 547.00000000)
( 1,
6)
N
1 ( 1.00000000)
( 1,
7)
C
.NULL.
( 2,
1)
N
1526 ( 1526.00000000)
( 2,
2)
C
"Connectivity error: [Microsoft][ODBC SQL Server
Driver][SQL Server]UPDATE statement conflicted with
COLUMN CHECK constraint 'CK authors au_id08EA5793'.
The conflict occurred in database 'pubs', table
'authors', column 'au_id'."
( 2,
3)
C
"[Microsoft][ODBC SQL Server Driver][SQL Server]The
statement has been terminated."
( 2,
4)
C
"01000"
( 2,
5)
N
3621 ( 3621.00000000)
( 2,
6)
N
1 ( 1.00000000)
( 2,
7)
C
.NULL.
As you can see from this output, the array contains two rows. The first reports the violation of the CHECK constraint, and the second tells you that SQL Server has terminated the statement.
While you are developing your application, you can show the contents of the second or third columns of this array, since only you (or other developers) would ever see the message. However, if your end users are anything like the folks that we’ve encountered, they willsurely react with panic to a message like that one! Therefore, you will eventually want to capture an error like this and “translate” it into something more friendly before putting the application in front of them.
This is where the trouble begins, as there is no easy way to uniquely identify the errors you receive back from SQL Server. For example, notice that the fifth column of the first row of the array contains the SQL Server error number 547. If you check this error number in
the Help system of SQL Server, you will find that this error is reported for any kind of constraint violation, not only CHECK constraints. This means that the only other way to determine the exact cause of the error is to parse the character string in the second or third column of the array.
In deciding which column to use, notice that the first and second rows of the array have the same exact error message in the second column, but the third column reports a different error
for each row.
Regardless, using the contents of the error array to create a user-friendly message poses a small problem that can really only be solved in one of two ways. The first is to search for the
constraint name (in this case, CK
authors
au_id
08EA5793) and present a friendly
message for each. While this seems easy enough, it requires that you always have an up-to-date list of constraints that are on the server and details about their meaning. If the constraint is modified at any time, you will have to update your client-side code to match. Also, refer tothe earlier note about how this constraint name came to exist. If someone simply regenerates the schema of the database or the table, the name change could break all of your code.
A second approach to error handling is a radical change over this first approach: Do not update the data on the server through views, but use stored procedures instead. The first step to making this approach work is to define a stored procedure for handling the updates ofauthor records, as in the following T-SQL code:
/* Add the message */ EXECUTE sp_AddMessage
@Msgnum = 50001,
@Severity = 16,
@Msgtext = 'Invalid author ID specified. Use the format
999-99-9999 for author IDs.'
GO
/* Create the stored procedure */ CREATE PROCEDURE UpdateAuthors
@Key Varchar(11) = NULL,
@Au_ID Varchar(11) = NULL
AS
IF @Key IS NULL
RAISERROR ('You must provide the @Key parameter when
calling UpdateAuthors',16,1)
ELSE
IF @Au_ID IS NOT NULL
IF @Au_ID LIKE '[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]'
UPDATE Authors
SET Au_ID = @Au_ID
WHERE Au_ID = @Key
ELSE
RAISERROR (50001,16,1)
ELSE
RAISERROR('No update occurred since @au_id was not provided',16,1)
go
This code starts by creating a system message that is stored in the master database of SQL Server. This makes the message available to all databases, and it can be easily invoked by the RAISERROR function. After the message is created, the stored procedure code canreference the message through its unique error number (in this case, 50001). Note that this code merely creates the system message and the stored procedure—it does not handle any kind of update of an author record, nor does it actually execute the stored procedure.
With this procedure in place in the pubs database, instead of posting the update directly through a Visual FoxPro view, you can now use a SQL pass through call:
*--turn off the non-trappable warnings
SQLSetProp(lhConn,"DispWarnings",.F.)
*--execute the stored proc to try the update
lnResult = SQLExec(lhConn,"EXECUTE UpdateAuthors "+ ;
"@key = 'keyval', @au_id = 'new value'")
IF lnResult < 0
*--something failed, check it out
lnRows = AERROR(laError)
*--more code normally follows, but is omitted here
The SQL pass through statement produces an error that can be captured from within Visual
FoxPro with the AERROR array. In this case, the error array looks like this:
LA
Pub
A
( 1,
1)
N
1526 ( 1526.00000000)
( 1,
2)
C
"Connectivity error: [Microsoft][ODBC SQL Server
Driver][SQL Server] 'Invalid author IDspecified. Use
the format 999-99-9999 for author IDs."
( 1,
3)
C
"[Microsoft][ODBC SQL Server Driver][SQL
Server] 'Invalid author ID specified. Use theformat
999-99-9999 for author IDs."
( 1,
4)
C
"37000"
( 1,
5)
N
50001 ( 50001.00000000)
( 1,
6)
N
1 ( 1.00000000)
( 1,
7)
C
.NULL.
Therefore, if the stored procedure is programmed with these “friendly” error messages, you can simply display these messages directly from the AERROR array. Furthermore, you can
trap for each specific error number (in this case, 50001) and translate the error message if desired. However, to get this kind of information, you will need to use stored procedures and forego the convenience of using views.
A third alternative is to not use any data validations on the server at all, and instead handle all of them from within the client application. This approach performs well, but only if you are writing the sole application that will ever touch the data on the server. Leaving thedatabase open with no validation whatsoever is typically not a good idea, as it makes it very easy for bad data to enter the system.
Decisions, decisions… The choice is up to you. One major factor in your decision may depend on how much access you have to the server. If you are the entire development department, and you take over the SQL Server and the client-side application development, then youcan choose to either design your own custom stored procedures for the updates, or monitor the naming of constraints and other rules so you can capture them when using views.
However, you may work in an environment where you do not “own” all of the pieces of the application. For example, the SQL Server may already be in use by another department. This department claims ownership of the server and the associated database that you need to access in order to write your Visual FoxPro application. Since the database belongs to the other department, you may have political problems in acquiring the necessary access to the database(s) on that server. Without proper access, you will have a tough time determining the
current status of any rules, stored procedures and so on. This can definitely complicate matters, and may force you to resort to error-handling techniques that otherwise would not be your
first choice.
Conflict resolution
No, this is not a section on how to deal with difficult employees or your “significant other.” It
is, however, meant to introduce another big difference between Visual FoxPro and SQL Server:
how they handle update conflict resolution.
When using native Visual FoxPro data, you have some choices when deciding how to deal with update conflicts. Recall that this error occurs when two users have pending changes on the same record and then attempt to commit their changes. Only one user can update at atime, so when the first user finishes his or her update, the second user is left with a problem because the data on disk is not the same as it was when the edit began. You can control Visual FoxPro’s behavior through the second parameter of TABLEUPDATE(), which allows you tomodify
how the second user experiences this problem.
If you set the second parameter to False, the second user’s update fails with error 1585, “Update conflict,” as Visual FoxPro will detect that the first user updated the disk values while the second user was waiting with pending changes. On the other hand, if you set the second parameter to True, the second user is permitted to overwrite the first user’s changes without questions or errors. Informally known as “last one in, wins,” this avoids the update conflict problem entirely. This is a great choice if it is not common for two users to edit the same recordsconcurrently, and it reduces the amount of error-handling code that you have to write.
When working with remote data, the same options are available and operate similarly. What changes is how you handle an update conflict error (i.e., use False for the second parameter). When a conflict occurs against Visual FoxPro data, you can use the OLDVAL() andCURVAL() functions to view the state of a field before it was edited and the actual value on disk, respectively. This allows you to go so far as to show the user what changes were made by the other user and let them evaluate how to proceed.
However, when dealing with remote data, CURVAL() is worthless, as it always returns the same value as OLDVAL(). Therefore, you have to use a completely different technique to resolve conflicts when working with remote data. Since CURVAL() does not work, you have tofind a different way to get at the actual data on the server. You may first think that REQUERY() is the answer, but this cannot be done on a view that has pending changes.
The only technique that seems to work is to open a separate copy of the data in its own work area, either with SQL pass through or with the view. You still have to use the REQUERY() function after opening this second copy to ensure that you’re looking at the latest values.This is due to the way that Visual FoxPro caches the view’s contents. But once you have opened the view and executed REQUERY(), you can retrieve the current values and use them as part of your conflict resolution.
Before we continue, we should point out another subtle difference that exists between updates against native data and updates against remote data. Recall that TABLEUPDATE() can detect update conflicts with its second parameter set to False. When applied to native data, an update conflict occurs even if the two users edited different fields. This is due to the way Visual FoxPro implements data buffering—a comparison of the entire row takes place, instead of only checking the modified fields. To check whether there is truly an update conflict, you must write a handful of code that employs the use of the OLDVAL() and CURVAL() functions. However, when using views (either local or remote, actually), you can choose a WhereType that ensures only the modified fields are checked against the back end for update conflicts.
For example, when your WhereType is set to the default of 3 (key and modified fields), Visual FoxPro will submit an UPDATE statement that only includes the fields that were modified by the current user. As long as the other user did not edit any of these same fields, no conflict exists. However, if you use the WhereType of 2 (key and updatable fields), you are bound to hit update conflicts more readily, as this will include any fields marked as updatable.
Note that choosing a WhereType of 4 (key and timestamp) is going to catch any update conflict in the entire record, as the timestamp will be updated regardless of which field or fields were changed. However, if you need to detect changes in any field (particularly memo fields), this proves to be the most efficient option.
Finally, if you wish to avoid update conflicts entirely, you can choose a WhereType
of 1 (key fields only), so that Visual FoxPro only cares about matching the key fields before posting the update. This has the same effect as specifying True for the second parameter of TABLEUPDATE().
View errors
There are many possible errors that ODBC can return when a view update fails. There are three main categories of errors: Something is wrong with your view, something is wrong with the data, or something is wrong with the server. You’ve already seen how data errors
happen, and with server errors, a Visual FoxPro error occurs that you can trap with traditional error handling.
During development, you are most likely to run into errors in your views. Most of these errors will be among those listed in Table 3.
Table 3. Common ODBC error messages indicating errors in views.
Error message
Action
No update table(s) specified. Use the Tables
cursor property.
Use the Tables property to specify at least one server
table, or modify the Tables property.
No key column(s) specified for the update
table table_name. Use the KeyFieldList cursor property.
Set the KeyField property to .T. for at least one field,
or use the KeyFieldList property of the view.
No valid update table specified for column
column_name. Use the UpdateNameList and
Tables cursor properties.
Set the table qualifier for the UpdateName property
for the column or for the UpdateNameList propertyfor the view.
The KeyField List cursor property doesn’t
define a unique key.
Set the KeyField property to .T. for at least one field,
or use the KeyFieldList property of the view.
One frustrating issue with some of these is the use of the ownership qualifier dbo for tables. Sometimes you’ll get an error message such as the first one in Table 3—“No update table(s) specified. Use the Tables cursor property.” So you check the value of the property, expecting it to be empty, but it says something like this:
dbo.category
This is exactly what it should be for the category table. We don’t know why, but sometimes VFP chokes when dbo is used and vice versa. So, if you get this message and you used dbo, then change the property to remove dbo. If the property doesn’t contain dbo, then changethe property to add it. This error doesn’t happen very often, but you can’t guess how many hours it will cost you to find it the first time! We have had the error occur with both SQL Server 6.5 and 7.0, but not yet with SQL Server 2000.
Debugging tools
Now that you have the general idea of how to handle and report errors in your client/server application, you need to learn how to debug errors that originate in any part of the application.
On the client side, you can continue to use your familiar Visual FoxPro debugging tools: the Debugger, Event Tracker, Coverage Profiler and, of course, your collection of past experience. But the client side is only one of three places where problems can crop up. The othertwo are in the ODBC driver and on the SQL Server. These two pieces have their own ways of debugging: ODBC logs (or trace) and some SQL Server tools.
SQL Server Profiler
One of the most useful tools for debugging tricky client/server problems is the Profiler, which is installed with SQL Server. You can use the Profiler to monitor events on a SQL Server. The Profiler will capture data about each event and allow you to either view that data in the Profiler’s graphical environment or save the data in a file or a SQL Server table for later viewing. You might be surprised by how many events take place on a SQL Server; fortunately, the Profiler also allows you to filter for certain types of events.
The Profiler can trace numerous types of events. Here is a partial list:
• Login connections, failures and disconnections
• Transact-SQL statements
• Lock acquisition and release
• Beginning or end of a stored procedure
• Beginning or end of statements in stored procedures
• Errors
• Beginning or end of SQL batches
• Beginning or end of transactions
• Remote procedure calls
By using the Profiler, you can examine the commands that VFP sends to SQL Server, evaluate the performance of individual commands or stored procedures, and even debug T-SQL stored procedures one line at a time.
The Profiler can be found in the Microsoft SQL Server file group. To create a trace, open it and select File | New | Trace. Figure 1 shows the resulting Trace Properties dialog. All you need to do here is name the trace. In the example shown in Figure 1, the trace is called MyTrace.
Figure 1. The SQL Profiler Trace Properties dialog.
Figure 2 shows a simple trace created by opening a view of the Tastrade category table created by the SQL Server Upsizing Wizard in Chapter 5, “Upsizing: Moving from File-Server to Client/Server.” This view shows some of the interesting information that can be supplied by the Profiler. The Event Class column describes the server event. When VFP sends a query, the class is SQL:BatchCompleted, and the Text column next to it will show the actual SQL statement that was sent in the batch. Note that the SQL User Name column displays the default administrative login. Since this system is using NT security, the NT User Name appears as
well. The CPU column will show which CPU in a multi-processor server handled the request. SQL Server is multi-threaded, so each thread can be sent to a different CPU. In this case, however, it is running on a single-processor system, so only CPU 0 is used. The next three
columns are particularly useful, as they can be used to troubleshoot performance problems. These show the number of reads, the number of writes, and the duration in milliseconds of the event. Partially hidden is the Connection column, and further to the right, off the screen, is thedatetime of the event. The datetime column isn’t particularly useful, as we would rather use the duration column to troubleshoot performance.
Figure 2. A simple trace in the SQL Server Profiler.
The Profiler is a great tool for tracking down all kinds of issues. Consider the following example of using the Profiler to track down a particularly nasty performance problem in the way the SQL Server 6.5 product handled certain messages from the ODBC driver. In this situation, Visual FoxPro was used as the client application for a SQL 6.5 box that was quite powerful: multiple processors, high-throughput RAID array, and all the other toys. Nevertheless, for some reason, when an insert occurred to a SQL table with a Text field, the applicationappeared to freeze.
The client tried letting it run its course to determine if the insert would eventually complete or if it was a lock-up situation. After waiting two hours with no response, they asked for help. When it had been determined that the code was implemented correctly and that thedatabase schema was valid and efficient, the Profiler provided the answer—it supplied the pertinent information and explained why performance was so awful.
The table was rather large, with more than 200,000 records, and each record had a Text field. SQL Server 6.5 was quite wasteful with Text fields, as it allocated a single 2K page for each Text field, regardless of whether the field contained data or not (SQL Server
7 fixed this problem by allowing its 8K pages to be shared among multiple Text fields). Therefore, searches through Text fields were to be avoided at all costs, since each search
would require moving and searching through 400MB of data. Even on this heavy-duty machine, searching through that much data would be slow, particularly since there are no indexes on
Text fields.
What was happening was that SQL 6.5 received the update request and converted it into three statements. The first inserted all of the data, except the contents of the Text field. Instead
of using the actual Text data, the statement provided a unique value for the Text field.
Then, the next statement was the death knell: it was performing a SELECT statement with a WHERE clause to find the unique value in the Text field! (Wouldn’t you at least think it would try to find the record with the primary key of the table?) It did this because it would use another function called UPDATETEXT to place the data into the Text field—used in order to avoid other kinds of problems with sending large amounts of Text data across an ODBC connection.
Once this performance problem was discovered, it was easy to rewrite their update routine to solve the problem. Without the Profiler, there would have been no clue as to why the server would choke on something as apparently simple as an INSERT statement.
Another great use for the Profiler is as a learning tool. You can find out all sorts of neat things by trying a function in the Enterprise Manager or from Visual FoxPro, and then looking at the Profiler to see what happened. For example, imagine that you are not sure how to build a CREATE DATABASE statement in T-SQL. You can use the Enterprise Manager to create a new database, and then switch over to the Profiler to see what command(s) the Enterprise Manager submitted to the server. Here’s the output from trying this little test:
CREATE DATABASE [test] ON PRIMARY
(NAME = N'test_Data',
FILENAME = N'C:\MSSQL7\data\test_Data.MDF' ,
SIZE = 1,
FILEGROWTH = 10%)
LOG ON
(NAME = N'test_Log',
FILENAME = N'C:\MSSQL7\data\test_Log.LDF' ,
SIZE = 1,
FILEGROWTH = 10%)
You may also uncover some undocumented features or discover how to do something programmatically that you thought could only be done through the Enterprise Manager. For example, we once used the Profiler to help troubleshoot a problem with the Upsizing Wizard before the source code for the wizard was available.
The SQL Server Performance Monitor
Another useful tool for tracking down performance problems on the server is the Performance Monitor, shown in Figure 3. This tool is used to view details about a wide variety of “counters” available through SQL Server. For example, you can see if your processors are always running at maximum throughput, if SQL Server is constantly swapping data in and out of the buffer cache, if you are using large amounts of locks or other resources, or even how many users are logged into the system at any given time.
As you can see, the Performance Monitor is best at telling you whether your SQL
Server hardware and software are configured properly for the typical load it needs to handle. You can log the activity as well, so it can be reviewed over time to help discover any possible degradation.
Figure 3. The SQL Server Performance Monitor utility.
ODBC logs
Although not our favorite tool for debugging a client/server application, ODBC trace is sometimes the only way to determine whether the problem lies between Visual FoxPro and SQL Server. For example, perhaps you believe that SQL Server is receiving an ill-formed statement that is causing an error that you just cannot seem to track down within either Visual FoxPro or SQL Server.
We don’t jump at the chance to use ODBC logs, because they are tremendously large due
to the detailed information stored therein, and it is quite tedious to wade through them. Because
C++ programmers, not Visual FoxPro developers, develop ODBC drivers, the logs they produce are full of hex values, but you can also view function calls and their success codes. This permits you to view the exact steps that ODBC takes to get data from the server and to put databack onto it. Armed with this information, you may be able to determine the cause of the problem you are having and perhaps come up with a workaround for it.
Depending on how you are accessing the server, there are two places where you can request creation of an ODBC log. First, if you are using a DSN, there is a check box where you can enable tracing (see Figure 4). Even if you are not using a DSN, you can also use the Tracingtab to enable an ODBC trace (see Figure 5).
Once you turn on ODBC logging, it will log every statement that occurs across the ODBC connection until it is disabled. Since this generates a large amount of information, you will want to turn it off as soon as you have logged the desired functions.
Figure 4. Enabling ODBC logging in an ODBC Data Source.
Figure 5. Enabling general ODBC logging.
One big problem with using an ODBC trace is the amount of time it takes to create the file. For example, what if you were to trace the opening of a view that retrieves data from the
authors table in the pubs database? This table has a “whopping” 23 records, and contains records that are no more than 151 bytes wide. The table uses mostly varchar fields, so the
record width is variable and is usually much smaller than 151 bytes. Worst-case scenario means that the table is 3473 bytes (or 3.4K) at its largest possible size.
When this view is opened under normal circumstances, it opens almost instantaneously. However, when the ODBC trace is enabled, opening the view will take 28 seconds. Clearly, the additional time is required to report the wealth of information produced by the ODBC trace to the log—the resulting log file is 178K!
This log file contains several useful bits of information. The first part of the file shows how the ODBC driver connects to the server, changes the database context, and sets a variety of options, including the timeout values set by the Visual FoxPro connection definition used by
the view. Once the connection is complete, the log shows that the SQL statement “SELECT * FROM dbo.Authors Authors” was executed. The rest of the log contains the steps used to retrieve the column data types, and their values for each record.
Therefore, you can use the ODBC log to produce a high level of detail on how Visual FoxPro is communicating through the ODBC driver to talk to SQL Server. However, in our experience, the ODBC log has been a “last resort” debugging tool. In most cases, if the problem is not something easily traced within the Visual FoxPro debugger, the next step is to use the SQL Server Profiler.
Summary
In this chapter, you have seen the details of handling errors and how to go about debugging problems. Errors are handled differently in a client/server application, and regardless of the development product you choose, you must decide where to actually handle errors. In Visual FoxPro, you can perform all error handling in the client application and only send clean data to the server, you can use views and capture TABLEUPDATE() errors, or you can discard views and use stored procedures on the server and SQLExec() statements.
Of course, you can still use the Visual FoxPro debugging tools to track down any Visual FoxPro errors. However, when the problems seem to be outside of Visual FoxPro, SQL Server and ODBC both provide some good tools for watching all of the activity generated by your Visual FoxPro application.
In the next chapter, you will see more information on how to design a client/server application in order to keep it flexible, scalable and maintainable.
No comments:
Post a Comment