FACS (File Access Control System: files meaning database tables, in the current context) is the ADAPL subsystem for searching and/or editing other* Collections database tables than the currently opened database table in Collections from which the adapl is started. (The currently opened database table is the one you are working in in Collections, so when viewing, editing or marking records, when the adapl is started.)

FACS refers to a Collections database table or dataset therein, using logical names. For Collections, these logical names are defined in the adapl itself: this method can be used both for adapls started in Collections and for stand-alone adapls.

The FACS instruction set:

File declaration: FDSTART, field declarations, FDEND
File access: OPEN, CLOSE, CLOSEALL
File search: READ
File maintenance: WRITE, DELETE
Other: CLEAR, UNLOCK, LOCK, ISIN, RECCOPY

Declaring a FACS name

In an adapl you start by declaring a FACS name, with FDSTART.

With the declaration (after FDSTART) you can also define database variables. The fields to be used (tags) are declared, with an alias for each field if required. The format of a FACS tag declaration is:

tag IS variable_name

By tag we mean the tag as it occurs in the declared database defnition. The variable name is the name (alias) by which the field will be known and called  in this ADAPL procedure. It is only necessary to assign an alias if a tag also occurs in another used database table in this procedure. However, it is always allowed. ADAPL programs are much clearer to read if you use aliases, which can consist of up to 32 characters, instead of tag names that can only have two. In an ADAPL program, each alias can only have one meaning.

Example 1

na IS name

ad IS address

pl IS place

Collections distinguishes between upper case and lower case letters, so you must be consistent in your spelling of the variable names!

After defining possible database variables you close the FACS declaration with FDEND.

If you don't want/need to use database variable aliases, you still have to declare the tags you want to use in this adapl for this FACS definition. Instead of assigning a database variable to each tag, you now just sum up the tags (one tag on each line) between FDSTART and FDEND. (And you don't need to use IS.)

Database table access

To be able to use a database table with an (earlier declared) FACS name you need to open the FACS database with OPEN. Under the hood, the index and record pointers are moved to the starting position, and Collections will initialize all database variables for this FACS database to null. The database table and accompanying variables then become available for FACS operations.

Record buffers

The database variables with their contents for a single record are stored in a so called FACS buffer; each opened FACS database table has its own buffer. When you open a FACS file, this record buffer is empty because all the variables are set to null. You have to read a specific record or assign values to FACS variables to fill the FACS record buffer with it.

The currently opened record in your Collections application (not an opened FACS record) during execution of the adapl (for instance in the case of the execution of a before-storage or after-field adapl), is always accessible in the adapl because the current record is stored in another local record buffer called adlib_lite. Just use tags from this local record to access them without FACS (so no FACS declaration needed for this).

If no record is currently in edit mode in your Collections application, but you are executing an adapl which reads records from the currently opened database table piece by piece, a print adapl for instance (also see Using ADAPL programs), then the currently read (local) record is stored temporarily in an implicit FACS buffer called _LOCAL.

So if you retrieve data from a currently opened record in a FACS database, using an adapl, then in the background, ADAPL uses a FACS buffer to store that record temporarily. There are three ways of gaining access to that data via ADAPL, namely: 1. by including the tags from that record in a FACS declaration and addressing the aliases, 2. via tag indirection, or 3. (available from 6.5.2) by using the record buffer name (the FACS name or adlib_lite), followed by an exclamation mark and the tag you wish to address. Here are four examples of the third method (assume Catalogue and People are declared FACS databases):

errorm Catalogue!ti ...

If (len(Catalogue!ti) = 0) { ...

errorm adlib_lite!do ...

if (enumval$(People!do, People!do, -1) = 'AUTHOR'){ ...

You can change the values in FACS-declared tags or aliases all you want: those changes won't ever be stored in the relevant record automatically. Only an explicit WRITE of the relevant FACS database will store any changes made to the buffer in the record read by FACS.

The situation is different for the record currently opened in edit mode in your Collections application during execution of the adapl. Although its contents are stored in the adlib_lite record buffer, any changes you make to values in tags will appear in the opened record immediately, without having to explicitly write the record. The new values will of course only be actually stored, if the user saves the record (or was already saving the record) and the new field contents pass validation and such.

However, when you want to write changes to records in the currently opened database in your application (not an opened FACS database) from within a print adapl or other adapl which is not executed while a record is in edit mode, you do have to write these changes explicitly. (This is because if no record is in edit mode, the adlib_lite buffer will be empty.) In this case you'll have to treat the local buffer as a FACS record. You address it with the non-declared "FACS" name _LOCAL. Use a WRITE to _LOCAL to write the changes to the currently read database record.

Sometimes in an adapl you need to be able to compare the new contents of a currently edited field in an existing record in Collections to the contents of the field before it was being edited (the previously stored content), maybe just to acknowledge the fact that the field contents have changed and have the adapl take some other action automatically and/or maybe because the difference between the new and old value is relevant somehow and needs further action. Maybe someone changes the insurance value of a painting and maybe the difference between the old and new value is so great that flags need to be raised, for example.

Normally you would program this in an after-field adapl or before-storage adapl, by FACS reading the current record - giving you the previously stored version of the record - after which the adapl may compare the current value to the "original" value. Or you would use the ISMODIFIED function to just check if field contents have changed, without getting the original value.
From Collections 1.15, there's new ADAPL functionality which removes the need for a FACS READ action in such cases altogether, boosting performance of such old/new field contents comparisons. The previously stored version of the record is now always available in an implicit, reserved FACS buffer called _original. You never have to declare or open this FACS buffer. To obtain the original contents of a record field from this buffer, you simply use the tag indirection syntaxis behind the FACS name: an exclamation mark followed by the field tag. In an after-field adapl for example, to obtain the previously stored object number (tag IN) from the currently edited record after leaving this field and present a warning message on screen if the object number has changed, could be done with:

if (&1 = 21) {
 if (&4[2] = 'IN') {
   if (_original!IN <> IN) {
         errorm 'You have changed the object number from ' + _original!IN + ' to ' + IN
   }
 }
}

Notes:

The retrieved original contents must be seen as a (text) string, just like the contents from a field tag or other FACS variable. (If the string constitutes a number, use VAL to convert that number to an actual numerical value with which you can do calculations or numerical comparisons.)
Only for existing records is the _original FACS buffer filled, not for new records.
The execution moment of an _original contents retrieval is irrelevant, but after-field, after-screen or before-storage would make most sense of course.
The _original FACS buffer is read-only so you can't write anything to it nor delete it. Any attempt in an adapl to do so will result in an on-screen error message.

Database table search

To search through a database table declared with FACS you use the READ instruction. You can read database tables as well as saved search tables (aka pointer files).

Database table maintenance

During a READ ... UPDATE, a found record is being 'locked' to prevent others on your system to delete or edit this record while your adapl wants to perform some action on this record. After a WRITE or DELETE on the current record the lock on it is automatically removed. When no action is performed on a found record you must unlock the record with UNLOCK.
A READ without UPDATE, just reads a records without locking it, so then it can still be edited by others too and you'll still have to LOCK the record explicitly just before a WRITE or DELETE.

After a system crash or when using adapls that do not correctly unlock records after locking them, you might find it impossible to edit certain records when working in a Collections application. Errors 34 or 85 or messages appear telling you that a record is already in use or that no record lock can be applied. These record locks must then first be removed before you can edit or delete the record. Preferably, use the Record lock manager tool for this purpose.

Use WRITEEMPTY to create an empty record in the database table referred to by facsname. With this you can obtain the record number of this record before you store it.

With DELETE you remove the current record from the database table.

Other FACS instructions

Function

Short description

UNLOCK

Removes the write lock to allow others to edit it again.

LOCK

Locks a record to edit or delete it.

CLEAR

Clears all database variables declared for the corresponding facsname from the record.

ISIN

Checks whether a priref is in a dataset.

RECCOPY

Copies a FACS record to a different FACS database.

FACS database closing

After your work on FACS databases in an adapl is done, you need to close the files you opened earlier: use CLOSE or CLOSEALL. The result code of the operation is put in the system variable &E. The FACS database and corresponding variables are then no longer available. If any record locks remain, Collections will display a warning message and removes the locks.

Note that FACS databases are closed automatically as well, after processing of the adapl is done. To not use CLOSE explicitly may speed up processing because opening a file again and again for each record in a selection of records to be processed takes time.

FACS error handling

The reserved variable &E is given a status code after each FACS instruction. If an instruction succeeds, then &E=0; any other values indicate that the operation was not successful. Typical reasons for the failure of a FACS operation include: no records were found using the given search key (&E=7), and, a record was found but could not be locked (&E=34). With READ, &E=55 means that the file could not be opened, and with OPEN, that the database table name or FACS name was incorrect.

You can look up the exact meaning of any value of &E after a FACS instruction in the list of error codes. Often, the exact reason for failure of an operation is not relevant, and it suffices to check whether &E equals zero.

The variable &E can also be written to by the ADAPL program. It is therefore important to check &E immediately after every FACS instruction.

When the program ends, the last value of &E is returned to the calling program as exit code. Unless you assign a different value to &E in the ADAPL program, the result of the last FACS instruction is passed to the calling program.