Updating Data
To update data, you can use buffers, transactions, or views.
Performing Updates with Buffers
After choosing the buffering method and the type of locking, you can enable record or table buffering.
To enable buffering
Choose one of the following options:
In the Form Designer, set the BufferModeOverride property of the cursor in the data environment of the form.
-or-
In code, set the Buffering property.
For example, you can enable pessimistic row buffering by placing the following code in the Init procedure of a form:
CURSORSETPROP('Buffering', 2)
You then place code for the update operations in the appropriate method code for your controls.
To write edits to the original table, use TABLEUPDATE( ). To cancel edits after a failed update operation in a table constrained by rules, use TABLEREVERT( ), which is valid even if explicit table buffering is not enabled. To specify a level of table integrity checking lower than the default setting, you can use the SET TABLEVALIDATE Command.
The following sample demonstrates how to update records when pessimistic record buffering is enabled.
Example of Updating Using Record and Table Buffers
Code | Comment |
---|---|
|
In the form Init code, open the table and enable pessimistic record buffering. |
|
Go through fields, checking for any field that's been modified. Note This code might be in the Click event of a "Save" or "Update" command button. |
|
Locate the next modified record. |
|
Present the current value and give the user the option to revert the change to the current field. |
|
SKIP guarantees that the last change is written. |
Managing Updates with Transactions
Even with buffering, things can go wrong. If you want to protect update operations and recover from an entire section of code as a unit, use transactions.
Adding transactions to your application provides protection beyond Visual FoxPro record and table buffering by placing an entire section of code in a protected, recoverable unit. You can nest transactions and use them to protect buffered updates. Visual FoxPro transactions are available only with tables and views contained in a database.
Wrapping Code Segments
A transaction acts as a wrapper that caches data update operations to memory or to disk, rather than applying those updates directly to the database. The actual database update is performed at the end of the transaction. If for any reason the system cannot perform the update operations on the database, you can roll back the entire transaction and no update operations are performed.
Note Buffered update operations made outside a transaction are ignored within a transaction in the same data session.
Commands that Control Transactions
Visual FoxPro provides three commands and one function to manage a transaction.
To | Use |
---|---|
Initiate a transaction | BEGIN TRANSACTION |
Determine the current transaction level | TXNLEVEL( ) |
Reverse all changes made since the most recent BEGIN TRANSACTION statement | ROLLBACK |
Lock records, commit to disk all changes made to the tables in the database since the most recent BEGIN TRANSACTION, and then unlock the records | END TRANSACTION |
You can use transactions to wrap modifications to tables, structural .cdx files, and memo files associated with tables within a database. Operations involving variables and other objects don't respect transactions; therefore, you cannot roll back or commit such operations.
Note When using data stored in remote tables, transaction commands control only updates to the data in the local copy of the view cursor; updates to remote base tables are not affected. To enable manual transactions on remote tables use SQLSETPROP( ), and then control the transaction with SQLCOMMIT( ) and SQLROLLBACK( ).
In general, you should use transactions with record buffers rather than with table buffering, except to wrap TABLEUPDATE( ) calls. If you place a TABLEUPDATE( ) command in a transaction, you can roll back a failed update, address the reason for the failure, and then retry the TABLEUPDATE( ) without losing data. This ensures the update happens as an "all-or-nothing" operation.
Though simple transaction processing provides safe data update operations in normal situations, it doesn't provide total protection against system failures. If power fails or some other system interruption occurs during processing of the END TRANSACTION command, the data update can still fail.
Use the following code template for transactions:
BEGIN TRANSACTION
* Update records
IF lSuccess = .F. && an error occurs
ROLLBACK
ELSE && commit the changes
* Validate the data
IF && error occurs
ROLLBACK
ELSE
END TRANSACTION
ENDIF
ENDIF
Using Transactions
The following rules apply to transactions:
- A transaction starts with the BEGIN TRANSACTION command and ends with the END TRANSACTION or ROLLBACK command. An END TRANSACTION statement without a preceding BEGIN TRANSACTION statement generates an error.
- A ROLLBACK statement without a preceding BEGIN TRANSACTION statement generates an error.
- A transaction, once begun, remains in effect until the corresponding END TRANSACTION begins (or until a ROLLBACK command is issued), even across programs and functions, unless the application terminates, which causes a rollback.
- Visual FoxPro uses data cached in the transaction buffer before using disk data for queries on the data involved in transactions. This ensures that the most current data is used.
- If the application terminates during a transaction, all operations roll back.
- A transaction works only in a database container.
- You cannot use the INDEX command if it overwrites an existing index file, or if any .cdx index file is open.
- Transactions are scoped to data sessions.
Transactions exhibit the following locking behaviors:
- Within a transaction, Visual FoxPro imposes a lock at the time a command directly or indirectly calls for it. Any system or user direct or indirect unlock commands are cached until the completion of the transaction by ROLLBACK or END TRANSACTION commands.
- If you use a locking command such as FLOCK( ) or RLOCK( ) within a transaction, the END TRANSACTION statement will not release the lock. In that case, you must explicitly unlock any locks explicitly taken within a transaction. You should also keep transactions containing the FLOCK( ) or RLOCK( ) commands as brief as possible; otherwise, users could be locked out of records for a long time.
Nesting Transactions
Nested transactions provide logical groups of table update operations that are insulated from concurrent processes. BEGIN TRANSACTION...END TRANSACTION pairs need not be in the same function or procedure. The following rules apply to nested transactions:
- You can nest up to five BEGIN TRANSACTION...END TRANSACTION pairs.
- Updates made in a nested transaction aren't committed until the outermost END TRANSACTION is called.
- In nested transactions, an END TRANSACTION only operates on the transaction initiated by the last issued BEGIN TRANSACTION.
- In nested transactions, a ROLLBACK statement only operates on the transaction initiated by the last issued BEGIN TRANSACTION.
- The innermost update in a set of nested transactions on the same data has precedence over all others in the same block of nested transactions.
Notice in the following example that because changes in a nested transaction aren't written to disk but to the transaction buffer, the inner transaction will overwrite the changes made to the same STATUS fields in the earlier transaction:
BEGIN TRANSACTION && transaction 1
UPDATE EMPLOYEE ; && first change
SET STATUS = "Contract" ;
WHERE EMPID BETWEEN 9001 AND 10000
BEGIN TRANSACTION && transaction 2
UPDATE EMPLOYEE ;
SET STATUS = "Exempt" ;
WHERE HIREDATE > {^1998-01-01} && overwrites
END TRANSACTION && transaction 2
END TRANSACTION && transaction 1
The following nested transaction example deletes a customer record and all its related invoices. The transaction will roll back if errors occur during a DELETE command. This example demonstrates grouping table update operations to protect updates from partial completion and to avoid concurrency conflicts.
Example of Modifying Records in Nested Transactions
Code | Comments |
---|---|
|
Cleanup from other transactions. |
|
Establish environment for buffering. |
|
Enable optimistic table buffering. |
|
Change a record. Change another record. |
|
Start transaction 1 and try to update all modified records without force. |
|
If the update failed, roll back the transaction. Get the error from AERROR( ). Determine the cause of the failure. If a trigger failed, handle it. If a field doesn't accept null values, handle it. If a field rule was violated, handle it. |
|
If a record was changed by another user, locate the first modified record. Loop through all modified records, starting with the first record. Lock each record to guarantee that you can update. Check each field for any changes. Check the buffered value against the value on disk, and then present a dialog box to the user. |
|
|
|
If user responded "No," revert the one record and unlock it. |
|
Break out of the "FOR nField..." loop. |
|
Get the next modified record. |
|
Start transaction 2 and update all non-reverted records with force. End transaction 2. Release the lock. |
|
If the record is in use by another user, handle it. If a row rule was violated, handle it. If there was a unique index violation, handle it. Otherwise, present a dialog box to the user. |
|
End transaction 1. |
Protecting Remote Updates
Transactions can protect you from system-generated errors during data updates on remote tables. The following example uses a transaction to wrap data-writing operations to a remote table.
Example of a Transaction on a Remote Table
Code | Comment |
---|---|
|
Get the connect handle and enable manual transactions. |
|
Begin the manual transaction. |
|
Try to update all records without force. If the update failed, roll back the transaction on the connection for the cursor. |
|
Get the error from AERROR( ). |
|
If a trigger failed, handle it. |
|
If a field doesn't accept null values, handle it. |
|
If a field rule was violated, handle it. |
|
If a record was changed by another user, handle it. Loop through all modified records, starting with the first record. |
|
Check each field for any changes. Check the buffered value against the value on disk, and then present a dialog box to the user. |
|
If user responded "No," revert the one record. Break out of the "FOR nField..." loop. Get the next modified record. |
|
Update all non-reverted records with force and issue a commit. |
|
Error 109 indicates that the record is in use by another user. |
|
Error 1583 indicates that a row rule was violated. |
|
Error 1884 indicates that the uniqueness of the index was violated. |
|
|
|
Present a dialog box to the user. End of error handling. |
|
If all errors were handled and the entire transaction was successful, issue a commit and end the transaction. |
See Also
Buffering Data | Managing Performance | Programming for Shared Access | TABLEUPDATE( ) | TABLEREVERT( ) | Controlling Access to Data | Management of Conflicts