z/TPF - Group home

z/TPFDF Education Series: Basic Application Usage

  
Two developers programming together

This is
an entry in a series of blog posts detailing core concepts and practical knowledge for the z/TPF Database Facility Enterprise Edition (z/TPFDF) product. Each new post will build on the ideas and skills learned in the previous installments. 

In this blog post, we’ll discuss at a high level how your applications can interface with z/TPFDF databases. The examples in this post will be focused on using z/TPFDF from a C or C++ application; however, many of these concepts also apply to assembly language programs. In general, the z/TPFDF functions have names starting with "df" (e.g. dfopn), while z/TPFDF assembler macros have similar names which start with "DB" (e.g. DBOPN).

The Open-Close Sequence

When an application interacts with the z/TPFDF database, the application goes through a series of steps. First, the application opens a z/TPFDF subfile by using a dfopn() API. There are several versions of the dfopn() API, and each version accepts different parameters and provides slightly different behavior. Each dfopn() API also accepts an options parameter, specified by the dft_opt type, that allows you to further specify exactly how you want z/TPFDF to behave when it's processing the subfile. We will explore some of these options and APIs in further detail in a future installment of this series.

The dfopn() APIs return a pointer to a dft_fil structure, which is also known as a SW00SR slot or simply a SW00SR (often pronounced "swoo-zer"). The SW00SR contains several pieces of information about the subfile that is currently open, which we will explore later in this article. You will also use the SW00SR as a parameter to subsequent z/TPFDF API calls as a means of identifying the subfile that is currently open. Because many z/TPFDF APIs receive a SW00SR as input, you can have multiple subfiles open at once, each with their own SW00SR.

After you open the subfile, you can call dfred() to read the subfile's LRECs. Upon each call to dfred(), a pointer in the SW00SR is set to point to a copy of an LREC in memory. This LREC is considered the "current" LREC. z/TPFDF also provides support for selection criteria (also known as keys). When you set keys in your z/TPFDF application, calls to dfred() will only return LRECs that match the keys that you've set. By default, z/TPFDF will read through all LRECs that match the keys you've set, and will read LRECs sequentially, in the same order as they are stored in the subfile. However, the dfred() APIs also allow you to read LRECs in reverse order, or jump to the first or the last LREC that matches the keys you have set. We will explore keys and dfred() options in more detail in future installments of this series. 

In addition to reading the subfile, the application can update the subfile's LRECs. The following are the most basic z/TPFDF APIs, each of which performs some action on one or more LRECs.
  • dfadd(): Add one or more LRECs to a subfile.
  • dfdel(): Delete one or more LRECs.
  • dfrep(): Replace an LREC with another LREC.

Finally, when you are done working with the subfile, call the dfcls() API to close the subfile. The dfcls() API writes any outstanding changes for the subfile to DASD. The dfcls() API also marks the SW00SR slot as available for use by another dfopn() call.

Working With the SW00SR

As mentioned previously, calls to dfopn() return a pointer to a dft_fil structure, also known as a SW00SR. The SW00SR is the primary interface to the particular subfile that you've opened. Each of the common z/TPFDF APIs outlined above take a SW00SR as input in order to identify the subfile that you are performing the given operation on.

In addition, the SW00SR contains information about the subfile that can be useful in your applications. Note that some of the information in the SW00SR is not set by dfopn(), and will only be set after subsequent API calls. The following fields, among others, may be helpful to use in your applications.

  • sw00fad8: Prime file address of the subfile.
  • sw00pgo: Name of the program that opened the subfile.
  • sw00rec: Memory address of the current LREC.
  • sw00rtn: Return code from the last z/TPFDF API call. (You should check the value of sw00rtn after each z/TPFDF API call)

To learn about more fields in the SW00SR, refer to the "SW00SR fields accessible from an application" topic in the z/TPF Knowledge Center. It is important that your applications only use those fields in the SW00SR that are outlined in the "SW00SR fields accessible from an application" topic. IBM may move, change, or delete any field in the SW00SR that is not documented as being accessible from applications.

Examples

To illustrate some of these concepts, here are a few example z/TPFDF programs.
 
In this example, we open an existing subfile, read each LREC in the subfile, print the contents of each LREC to the console, and close the subfile.

#include <cdf.h>
#include <tpf/tpfapi.h>
// my_df_database.h defines the format of the z/TPFDF database that we are working with.
#include <my_df_database_type.h>

int ABCD()
{
  struct wtopc_header header_buffer;
  char msgText[256];

  // "my_lrec_type" is a structure representing the format of an LREC.
  // This structure should be defined in both a DSECT and a C header (my_df_database.h).
  // This LREC contains standard z/TPFDF fields, and only contains one user-defined
  //   field: a name.
  struct my_lrec_type *lrec;

  // Define the file ID that you are going to open.
  // You have to send a pointer to this value to dfopn(), so you cannot pass the
  //   literal value in. You must define a local variable containing the value.
  dft_fid fileID[2] = "\xB4\x31";
  
  // Open a subfile.
  // dfopn_acc accepts the following parameters: 
  // dft_ref *ref_name: Reference name of the file you are opening.
  // dft_fid *id      : File ID of the file you are opening.
  // dft_opt access   : The method you want to use to access the subfile.
  // dft_opt options  : Options for processing the subfile.
  // dft_xxx acc      : Identifying data used to access the subfile.
  // We are accessing by using the DFOPN_ORD option to open the first
  //   ordinal (or the first subfile) in the file.
  // By specifying 0 for the options parameter, we are telling z/TPFDF
  //   not to perform any special behavior when opening the subfile.
  dft_fil *sw00sr = dfopn_acc("DR31BI", fileID, DFOPN_ORD, 0, 1);
  
  // DF_ER is a macro provided by z/TPFDF that checks if an error has been
  //   indicated in the SW00SR's sw00rtn field.
  // If an error occurred while opening the subfile, print a message and exit.
  if (DF_ER(sw00sr))
  {
    wtopc_insert_header(&header_buffer, "ABCD", 1, WTOPC_ERROR, WTOPC_SYS_TIME);
    wtopc("An error occurred while opening the subfile.",
          WTOPC_EBROUT | WTOPC_PRC,
          WTOPC_NO_CHAIN,
          &header_buffer);
     exit(1);
  }

  // Read an LREC from the subfile.
  // dfred() accepts the following parameters:
  // dft_fil *file  : The pointer to the SW00SR that you want to read from.
  // dft_opt options: Options for processing the subfile.
  // dfred() returns a pointer to a dft_rec structure.
  // We can cast that to a my_lrec_type structure, as long as my_lrec_type is
  //   defined properly.
  lrec = (struct my_lrec_type *) dfred(sw00sr, 0);

  // In contrast to DF_ER, DF_OK checks that an error has NOT occurred.
  // So, this loop will keep reading until it encounters an error.
  // One of these "error" conditions could be that there are no records left in
  //   the subfile. We will check for that condition later.
  while (DF_OK(sw00sr))
  {
    // Because we've casted the LREC to our my_lrec_type structure, we can
    //  look at the fields that we've defined within it.
    sprintf(msgText, "Name: %s", lrec->vLrec.v3rec._name);
    // Print the name.
    wtopc_insert_header(&header_buffer, "ABCD", 2, WTOPC_INFO, WTOPC_SYS_TIME);
    wtopc(msgText, WTOPC_EBROUT | WTOPC_PRC, WTOPC_NO_CHAIN, &header_buffer);
    
    // Read the next LREC.
    lrec = (struct my_lrec_type *) dfred(sw00sr, 0);
  }

  // We've exited the while loop, so we have some kind of "error" condition.
  // Check if it's a real error, or if we've just run out of records to read.
  // DF_NR checks if there are no records to read.
  if (!(DF_NR(sw00sr)))
  {
    // We're not out of records; this is a real error.
    wtopc_insert_header(&header_buffer, "ABCD", 3, WTOPC_ERROR, WTOPC_SYS_TIME);
    wtopc("An error occurred while reading the subfile.",
          WTOPC_EBROUT | WTOPC_PRC,
          WTOPC_NO_CHAIN,
          &header_buffer);
  }

  // Close the subfile.
  // dfcls() accepts the following parameters:
  // dft_fil *file  : The pointer to the SW00SR that you want to close.
  // dft_opt options: Options for processing the subfile.
  // dfcls will never return any error indicators, so there is nothing to check.
  dfcls(sw00sr, 0);

  return 0;
}
In this example, we open an existing subfile, add an LREC to the subfile, and close the subfile.

#include <cdf.h>
#include <tpf/tpfapi.h>
#include <my_df_database.h>

int ABCD()
{
  struct wtopc_header header_buffer;
  struct my_lrec_type lrec;
  char *my_name = "Jane Doe";

  // Open a subfile.
  dft_fil *sw00sr = dfopn_acc("DR26BI", "B426", DFOPN_ORD, 0, 1);
  if (DF_ER(sw00sr))
  {
    wtopc_insert_header(&header_buffer, "ABCD", 1, WTOPC_ERROR, WTOPC_SYS_TIME);
    wtopc("An error occurred while opening the subfile.",
          WTOPC_EBROUT | WTOPC_PRC, WTOPC_NO_CHAIN, &header_buffer);
    exit(1);
  }

  // Set up the in-memory copy of an LREC.
  // Each LREC will usually contain a size and key field.
  lrec._siz = strlen(my_name) + sizeof(dft_siz) + sizeof(dft_pky);
  lrec._key = 0x80;
  strcpy(lrec.vLrec._nam, my_name);

  // Add the LREC to the subfile.
  dfadd(sw00sr, DFADD_NEWLREC, 0, &lrec);
  // Verify return value and print an error or success message accordingly.
  if (DF_ER(sw00sr))
  {
    wtopc_insert_header(&header_buffer, "ABCD", 2, WTOPC_ERROR, WTOPC_SYS_TIME);
    wtopc("An error occurred while adding an LREC.",
          WTOPC_EBROUT | WTOPC_PRC, WTOPC_NO_CHAIN, &header_buffer);
  }
  else
  {
    wtopc_insert_header(&header_buffer, "ABCD", 3, WTOPC_INFO, WTOPC_SYS_TIME);
    wtopc("Successfully added an LREC.",
          WTOPC_EBROUT | WTOPC_PRC, WTOPC_NO_CHAIN, &header_buffer);
  }

  dfcls(sw00sr, 0);

  return 0;
}
In the next installment, we’ll dive into more advanced database organization, including indexing. If you have any questions about what you learned in this article, get in touch with a member of the z/TPF development lab, or try consulting our documentation for z/TPFDF

Comments

Fri April 26, 2024 08:01 AM

Claire, this is again a great article. Really helps in understanding how TPF applcation programs can work with TPFDF.

As you mentioned in these blogs, did you publish more posts as part of this education series or do you plan to publish more?