Centura
Centura
Centura
20-7302-1002
Trademarks
Centura, the Centura logo, Centura net.db, Centura Web Developer, Gupta, the Gupta
logo, Gupta Powered, the Gupta Powered logo, Fast Facts, Object Nationalizer, Quest,
QuickObjects, SQL/API, SQLBase, SQLBase Exchange, SQLConsole, SQLGateway,
SQLHost, SQLNetwork, SQLRouter, SQLTalk, and Team Object Manager, RDM, ROM,
db_QUERY, db_REVISE and Velocis are trademarks of Centura Software Corporation
and may be registered in the United States of America and/or other countries.
SQLWindows is a registered trademark and TeamWindows, ReportWindows and
EditWindows are trademarks exclusively used and licensed by Centura Software
Corporation.
Adobe is a trademark of Adobe Systems, Incorporated.
IBM, OS/2, NetBIOS, and AIX are registered trademarks of International Business
Machines Corporation.
UNIX is a registered trademark licensed exclusively by The Open Group.
SCO UNIX is a trademark of Santa Cruz Operation, Incorporated.
Microsoft is a registered trademark, and DOS, Windows, Windows NT, and Windows 95
are trademarks, of Microsoft Corporation.
Java and Solaris are trademarks of Sun Microsystems, Incorporated.
Microsoft, Internet Explorer, Internet Information Server, DOS, Win 32, Windows,
Windows NT, Windows 95 and Visual Basic are either registered trademarks or
trademarks of Microsoft Corporation in the United States of America and/or other
countries.
Novell is a registered trademark, and NetWare is a trademark, of Novell Incorporated.
All other product or service names mentioned herein are trademarks or registered
trademarks of their respective owners.
Copyright
Copyright 2000 by Centura Software Corporation. All rights reserved.
RDM 5.0 Users Guide
20-7302-1002
May 2000
Contents
Chapter 1 Introduction
1.1
1.2
Introduction................................................................................................................... 2-1
Definitions ..................................................................................................................... 2-1
The Network Database Model .................................................................................... 2-4
Other Database Models ............................................................................................... 2-7
2.4.1
The Hierarchical Database Model...................................................................... 2-8
2.4.2
The Relational Database Model.......................................................................... 2-8
2.5
Advantages of the Combined Model......................................................................... 2-9
2.6
Access Methods in RDM ........................................................................................... 2-10
2.7
Elements of an RDM Database ................................................................................. 2-11
Introduction................................................................................................................... 3-1
System Components..................................................................................................... 3-1
ddlp ................................................................................................................................ 3-1
initdb .............................................................................................................................. 3-2
RDM Runtime Library ................................................................................................. 3-3
lock manager ................................................................................................................. 3-3
lmclear............................................................................................................................ 3-3
dbclrlb ............................................................................................................................ 3-3
ida ................................................................................................................................... 3-3
dal ................................................................................................................................... 3-3
dbcheck .......................................................................................................................... 3-4
dbedit ............................................................................................................................. 3-4
dbimp ............................................................................................................................. 3-4
dbexp.............................................................................................................................. 3-4
keybuild ......................................................................................................................... 3-4
keypack .......................................................................................................................... 3-4
prdbd.............................................................................................................................. 3-5
console............................................................................................................................ 3-5
Contents
Introduction................................................................................................................... 4-1
Database Definition Language (DDL) ....................................................................... 4-1
4.2.1
DDL Basics............................................................................................................ 4-1
4.2.2
Database Statement.............................................................................................. 4-3
4.2.3
Timestamp Statement.......................................................................................... 4-4
4.2.4
File Declarations................................................................................................... 4-5
4.2.5
Record Declarations............................................................................................. 4-7
4.2.6
Field Declarations ................................................................................................ 4-9
4.2.7
Set Declarations.................................................................................................. 4-13
4.3
DDL Processor Operation.......................................................................................... 4-16
4.3.1
DDL Processor Execution ................................................................................. 4-16
4.3.2
ddlp Alignment.................................................................................................. 4-20
Nested Structures ....................................................................................................... 4-21
4.3.3
ddlp Alignment in Windows............................................................................ 4-21
4.4
Database Design Considerations .............................................................................. 4-21
4.4.1
Logical Design Considerations......................................................................... 4-22
Use of Keys.................................................................................................................. 4-22
Use of Sets.................................................................................................................... 4-26
Use of Multiple Databases......................................................................................... 4-31
4.4.2
Physical Design Considerations....................................................................... 4-32
RDM File Structure..................................................................................................... 4-32
Record and Key Placement ....................................................................................... 4-33
ddlp File Structure Report......................................................................................... 4-34
File Page Sizes ............................................................................................................. 4-36
4.5
Database Design Example ......................................................................................... 4-36
4.5.1
Introduction ........................................................................................................ 4-36
4.5.2
Requirements...................................................................................................... 4-37
4.5.3
Database Design................................................................................................. 4-38
ii
Introduction................................................................................................................... 5-1
Database Control .......................................................................................................... 5-2
5.2.1
Opening and Closing Databases ........................................................................ 5-4
5.2.2
Operational Environment ................................................................................... 5-4
Path Specification in d_open....................................................................................... 5-5
Environment Variables ................................................................................................ 5-5
Environment Control Functions ................................................................................. 5-9
Substituting Database Files ....................................................................................... 5-10
5.2.3
Dynamic Database Initialization ...................................................................... 5-11
5.2.4
Runtime Control................................................................................................. 5-12
Maximum Open Files................................................................................................. 5-12
Size of Runtime Cache ............................................................................................... 5-13
Option Settings ........................................................................................................... 5-14
5.3
Currency Tables.......................................................................................................... 5-15
5.4
Data Retrieval.............................................................................................................. 5-18
5.4.1
Data Retrieval Using Keys ................................................................................ 5-19
5.4.2
Data Retrieval Using Sets.................................................................................. 5-23
5.4.3
Direct Access Retrieval...................................................................................... 5-31
5.5
Data Creation .............................................................................................................. 5-34
5.6
Data Modification....................................................................................................... 5-38
5.7
Data Deletion............................................................................................................... 5-39
5.8
Database Error Reporting.......................................................................................... 5-42
5.9
Multiple Database Access.......................................................................................... 5-44
5.9.1
Opening Multiple Databases ............................................................................ 5-44
5.9.2
Accessing Multiple Databases.......................................................................... 5-45
5.10 Context Switching ...................................................................................................... 5-47
5.11 International Character Sets ...................................................................................... 5-50
Introduction................................................................................................................... 6-1
Operational Environment............................................................................................ 6-2
Transaction Processing Functions .............................................................................. 6-3
Transaction Processing ................................................................................................ 6-4
Database Recovery ....................................................................................................... 6-5
6.5.1
External Recovery ................................................................................................ 6-6
6.5.2
Automatic Recovery ............................................................................................ 6-6
6.6
Archive Logging ........................................................................................................... 6-7
Introduction................................................................................................................... 7-1
Operational Environment............................................................................................ 7-3
Contents
iii
7.3
iv
Is Owner?..................................................................................................................... 8-36
8.2.5
Using the Record Menu Commands ............................................................... 8-36
Scan............................................................................................................................... 8-36
First............................................................................................................................... 8-37
Last ............................................................................................................................... 8-37
Next .............................................................................................................................. 8-37
Previous ....................................................................................................................... 8-37
Enter new record ........................................................................................................ 8-37
Modify.......................................................................................................................... 8-37
Delete ........................................................................................................................... 8-38
Disconnect & Delete ................................................................................................... 8-38
8.2.6
Using the Currency Menu Commands ........................................................... 8-38
Set Current Owner ..................................................................................................... 8-39
Set Current Member................................................................................................... 8-39
Set Current Record ..................................................................................................... 8-39
Display Timestamp Status......................................................................................... 8-39
Display Currency Tables ........................................................................................... 8-39
8.2.7
Using the Transactions Menu Commands ..................................................... 8-40
Begin............................................................................................................................. 8-40
End ............................................................................................................................... 8-40
Abort ............................................................................................................................ 8-40
8.2.8
Using the Locks Menu Commands ................................................................. 8-40
Lock Types................................................................................................................... 8-41
Free Types ................................................................................................................... 8-41
Set Record Lock Bit..................................................................................................... 8-41
Clear Record Lock Bit ................................................................................................ 8-42
Test Record Lock Bit................................................................................................... 8-42
Display Lock Status.................................................................................................... 8-42
8.2.9
Using the Special Menu Commands ............................................................... 8-42
Cache Pages................................................................................................................. 8-42
File Handles................................................................................................................. 8-42
Close Handles ............................................................................................................. 8-43
TAF and LOG File Paths............................................................................................ 8-43
Lock Manager ............................................................................................................. 8-43
8.2.10 Using the Options Menu................................................................................... 8-43
Closefiles...................................................................................................................... 8-44
Delete Chain ................................................................................................................ 8-44
Delete Log.................................................................................................................... 8-44
Ignorecase.................................................................................................................... 8-44
Prealloc......................................................................................................................... 8-44
Readonly ...................................................................................................................... 8-44
Remotesync ................................................................................................................. 8-44
Syncfiles ....................................................................................................................... 8-44
Contents
vii
Introduction................................................................................................................. 13-1
Formatted Data File Dump Utility (datdump) ....................................................... 13-1
Database Consistency Check Utility (dbcheck) ...................................................... 13-2
Delete Chain Sort Utility (dchain) ............................................................................ 13-3
Database Initialization Utility (initdb) ..................................................................... 13-3
Key File Build Utility (keybuild) .............................................................................. 13-3
Formatted Key File Dump Utility (keydump)........................................................ 13-4
Key File Pack Utility (keypack) ................................................................................ 13-4
Database Dictionary Print Utility (prdbd)............................................................... 13-5
Clear Lock Bit Utility (dbclrlb) ................................................................................. 13-5
ix
Tables
Table 1-1.
Table 3-1.
Table 5-1.
Table 5-2.
Table 5-3.
Table 5-4.
Table 5-5.
Table 5-6.
Table 5-7.
Table 5-8.
Table 6-1.
Table 6-2.
Table 7-1.
Table 7-2.
Table 7-3.
Table 7-4.
Table 7-5.
Table 7-6.
Table 7-7.
Table 7-8.
Table 7-9.
Table 7-10.
Table 8-1.
Table 8-2.
Table 8-3.
Table 8-4.
Table 9-1.
Table 9-2.
Table 12-1.
Table 13-1.
Table 14-1.
Table 14-2.
Table 14-3.
Table 14-4.
Table 14-5.
Table 14-6.
Contents
xi
Figures
Fig. 2-1.
Fig. 2-2.
Fig. 2-3.
Fig. 2-4.
Fig. 2-5.
Fig. 2-6.
Fig. 3-1.
Fig. 3-2.
Fig. 3-3.
Fig. 4-1.
Fig. 4-2.
Fig. 4-3.
Fig. 4-4.
Fig. 4-5.
Fig. 5-1.
Fig. 5-2.
Fig. 7-1.
Fig. 8-1.
Fig. 8-2.
Fig. 8-3.
Fig. 8-4.
Fig. 8-5.
Fig. 8-6.
Fig. 8-7.
Fig. 8-8.
Fig. 8-9.
Fig. 8-10.
Fig. 8-11.
Fig. 8-12.
Fig. 8-13.
Fig. 8-14.
Fig. 8-15.
Fig. 8-16.
Fig. 8-17.
Fig. 8-18.
Fig. 8-19.
Fig. 8-20.
Fig. 8-21.
Fig. 8-22.
xii
Fig. 8-23.
Fig. 8-24.
Fig. 8-25.
Fig. 8-26.
Fig. 8-27.
Fig. 8-28.
Fig. 8-29.
Fig. 8-30.
Fig. 8-31.
Fig. 8-32.
Fig. 10-3.
Fig. 12-1.
Fig. 12-2.
Fig. 12-3.
Fig. 14-1.
Fig. 14-2.
Fig. 14-3.
Fig. 14-4.
Fig. 14-5.
Fig. 14-6.
Fig. 14-7.
Fig. 14-8.
Fig. 14-9.
Fig. 14-10.
Fig. 14-11.
Fig. 14-12.
Fig. 14-13.
Fig. 14-14.
Fig. 14-15.
Contents
xiii
Chapter 1
Introduction
1.1
Overview
1-1
1.2
RDM Documentation
RDM documentation is a three-manual set and includes this Users Guide, the RDM
Reference Manual and the RDM Multi-User Guide. Companion books for supporting
utility products are the db_QUERY Users Guide, db_REVISE Users Guide, and the ROM
manuals. The full set is provided in PDF format and in hard copy by separate order.
This volume, the RDM Users Guide, contains general information and examples on the
use of the system.
1-2
Chapter 5, "Database Manipulation," explains the use of RDM runtime functions for
controlling, accessing, and manipulating a database from a C application program.
Chapter 6, "Transaction Processing," describes transaction processing functions and tells
you how to use transactions in a single- or multi-user environment. This chapter will
help you design transactions that meet the needs of your application.
Chapter 7, "Multi-User Database Control," describes the use of RDM in multi-user,
shared database applications. Basic multi-user concepts are presented, as is a detailed
description of RDMs multi-user implementation. Chapter 7 also explains how to use the
runtime multi-user control functions to efficiently synchronize shared database access.
Chapter 8, "Interactive Database Access Utility (ida and wida)," provides complete
information on the use of the command line ida utility for UNIX users, and the menudriven Windows version called wida.
Chapter 9, "RDM Unicode Support" explains the use of the Unicode features and how
RDM incorporates this character encoding standard.
Chapter 10, "File Transfer Utilities (dbimp and dbexp)," describes the utilities used for
transferring data between an RDM database and ASCII text.
Chapter 11, "Database Access Language (dal)," describes a tool for accessing a database
interactively or in batch mode. dal uses the RDM runtime library functions, plus a few
information and control commands. It is an excellent educational tool.
Chapter 12, "Database Editor (dbedit)," contains a description of the low-level database
editing utility, which allows viewing and changing a database at the "bits and bytes"
level. The database editor is the ultimate database repair program.
Chapter 13, "Maintenance Utilities," provides information on the use of the following
utilities: database initialization utility (initdb), database consistency check utility
(dbcheck), delete chain sort utility (dchain), key file build utility (keybuild), dictionary
print utility (prdbd), formatted data file dump utility (datdump), key file packing utility
(keypack), formatted key file dump utility (keydump), and the clear lock bit utility
(dbclrlb).
Chapter 14, "File Formats and Dictionary Tables," provides detailed information on RDM
data and key files, and on the structure and organization of the RDM dictionary.
The Glossary defines a number of database terms used in this manual and the other
books of the RDM set.
Introduction
1-3
1-4
Description
Bold Text
Italic Text
Courier Text
Brackets ([ ])
Ellipsis (...)
1.3
Introduction
1-5
Chapter 2
Database Concepts
2.1
Introduction
This chapter presents the basic database concepts of the RDM system. The database
terms used in this manual are defined in section 2.2. The network database model, which
forms the basis of the RDM system, is described in section 2.3. Section 2.4, "Other
Database Models," describes the hierarchical and relational database models. Section 2.5
describes the advantages of the network model over the relational database model.
Specific elements of an RDM database are described in the final section.
To use RDM productively, you need to understand RDM database concepts. This
chapter intends to provide sufficient information for a database novice to use RDM
effectively. As part of our efforts to familiarize you with database concepts, we have
provided a Glossary of database terms at the end of this manual.
2.2
Definitions
The basic unit of information in a database is a field. A field (or data field) is an item of
data with attributes such as name, type (for example, a character or an integer), and
length. Examples of fields are employee name, date of birth, social security number, inventory item code, and serial number. Other database systems or books may use other
terms (such as attribute, entity, or column) for field.
A record is a named collection of related fields, which are stored and accessed as a unit.
Other database systems or books may use other terms (such as table or file) for record.
For example, a record named check in a checking account database may have the
following fields:
date
check number
paid to
amount
Each occurrence (or instance) of a check record in the database contains a value for each
of these fields. The definition of a record (made up by its fields) is called the record type
and is similar to Pascal or COBOL records and C or PL/1 structures.
All occurrences of a particular record type are stored in an operating system file. Files
are the primary physical storage units of database organization. A database, therefore, is
a collection of related files.
Database Concepts
2-1
A key is a field through which rapid or sorted access to a record is possible. In the check
record, you might define check number as a key field, to allow quick retrieval of a check
record occurrence through specification of a check number.
An index is a file containing only keys. It is synonymously referred to as a key file. The
index to this manual demonstrates the features of a key file: the individual subject
entries in the index are the "keys," while the page where the subject is discussed is
analogous to the associated "records." You can find the page that discusses a desired
subject much more quickly by using the index than by reading through each page. And,
because the keys are sorted in the index, you can quickly find a specific key. Key files are
similar, except the computer does the sorting and look-ups for you. To maintain its key
files, RDM uses the B-tree method, one of the most efficient techniques for implementing
an index.
In a key scan operation, the keys in an index are read in the order they appear. Key scans
are used to produce sorted listings of records and for fast search operations requiring
inspection of a large number of record occurrences (for example, retrieving all checks
entered between two dates).
Data relationships often exist between record types. For example, the checking account
database may include budget categories. A second record type named budget could be
defined with the following data fields:
budget code (a key field)
category description
monthly allocation
balance
To associate a particular budget category with each check record, we add a budget code
field to the check record type, forming a relationship between the budget record and the
check record. Whenever a check is entered, the related budget record is located via the
budget code, and the balance for that budget is updated by the amount specified in the
check record.
The schema is the conceptual definition of the content and organization of a database. A
schema will include the definitions of all record types, with their fields and keys. The
form of the schema used by the DBMS is called the dictionary. In RDM (and most other
DBMSs) a Database Definition Language, or DDL, specifies the schema. An RDM DDL
specification for the checking account database is shown below. The specifics of the
actual DDL statements are explained in Chapter 4, "Database Design."
2-2
database ckngacct {
data file "chkng.dat" contains budget, check;
key file "chkng.key" contains code, check_no;
record budget {
key char code[6];
char cat_desc[48];
float allocation;
float balance;
}
record check {
key int check_no;
int check_date;
char bud_code[6];
char paid_to[48];
float amount;
}
}
A data model (or database model) is a conceptual representation of inter-record relationships. The relational database model establishes and maintains inter-record relationships
through common data fields. For example, in the checking account example a common
data field, budget code, establishes the relationship between the budget record and the
check record.
Other database models, in particular the network database model, establish inter-record
relationships directly, through physical links between the related records, rather than
through common data fields. These models are discussed in the following sections.
Since RDM supports both the relational and the network database models, you can
combine the features of these models to meet the needs of your particular application.
Database Concepts
2-3
2.3
In the network database model, the relationships between record types are explicitly
defined and directly maintained through sets. A set defines a one-to-many relationship
between two record types. Examples of sets are:
one baseball league has many teams
one baseball team has many players
Sets are implemented with linked lists of pointers to the record locations of the set
members and owners. The result is a network of interconnected records.
Figure 2-1 illustrates the set relationships for the baseball example. The boxes represent
instances of the league, team, and player record types. The arrows represent the links
that connect the related records.
American League West
League: Team set
Team: Player set
Seattle Mariners
K. C.
94
Royals
Texas
68
89
Rangers
73
83
79
Bradley, Scott
Brett, George
Davis, Alvin
Griffy, Jr.
Jackson, Bo
Tartabull, Danny
Reynolds, Harold
Wilson, Willie
2-4
Schema diagrams are used to illustrate graphically the inter-record relationships of the
database design. Figure 2-2 shows the schema diagram for the baseball example. In this
diagram (and in all other schema diagrams in this manual), the boxes represent record
types and the arrows represent set types. The league_team set forms a one-to-many
relationship between the league record type (called the owner of set league_team) and
the team record type (called the member of set league_team). The team_player set forms
a one-to-many relationship between the team record type and the player record type.
LEAGUE
LEAGUE_TEAM
Boxes
represent
records.
Arrows
represent
sets.
TEAM
TEAM_PLAYER
PLAYER
Database Concepts
2-5
In the checking account example discussed earlier, the relationship between the budget
and check record types could be specified using a set called transactions, defining a oneto-many relationship between a budget record (owner) and the check records (members)
written against a particular budget category. In this case, the bud_code field in the check
record would not be defined in the check record type, as it is redundant. The RDM DDL
would be modified as follows:
database ckngacct {
data file "chkng.dat" contains budget, check;
key file "chkng.key" contains code, check_no;
record budget {
key char code[6];
char cat_desc[48];
float allocation;
float balance;
}
record check {
key int check_no;
int check_date;
char paid_to[48];
float amount;
}
set transactions {
order last;
owner budget;
member check;
}
}
2-6
Any given record type can be the owner of any number of different sets and also a
member of any number of different sets. Thus, network structures like that shown in
Figure 2-3 are valid.
2.4
Two other major database models are the hierarchical and relational models. These are
described below.
Database Concepts
2-7
2-8
2.5
Relational model database systems have been extremely popular, primarily because the
simplicity of the underlying data model makes them easy to use. With the network
model, the primary benefits are better performance, reduced storage requirements, and
greater assurance of data integrity. Consider the diagram in Figure 2-5.
Table 1
Index
A B C
Table 2
C D E
Record 1
A
B
Set
Pointer
Record 2
D
E
C
Fig. 2-6. Network DBMS Overhead
Database Concepts
2-9
The network model eliminates data redundancy by relating the two record types directly,
without requiring the duplicate field and index file. Moreover, the related record is
accessed directly with one database read operation, where the relational model forces
you to read first an index and then the related record.
For those situations where an index is more efficient, RDM provides you with that
option. With RDM, network access and indexed access are independent methods, so you
can combine them to suit the needs of your particular application. Combining these
technologies gives you maximum database design flexibility.
2.6
We have already discussed two access methods available in RDM: indexes and sets. A
third method is called sequential access. All three methods can be used together for
navigating and searching in a database. Each has its own ways of establishing and
changing a position in the database. The methods are nearly orthogonal, meaning that
the use of one will not disrupt the use of the others. The one value they share is the
current record, which points to the record in the database that has been found most recently. The current record is the default object for many of the RDM functions. All three
access methods set the current record.
The indexed method allows you to find a record occurrence by supplying a key. The key
can be an exact match, in which case you are positioned directly on a record, or a near
match, which will position you just before the record containing a key value higher in the
collating sequence. You can also position yourself to the first or last keys of a given key
type, regardless of their values. Once at a position, you can move to the previous or next
key in the collating sequence. The keys are maintained and navigated in the order
maintained in the index, regardless of the physical order of the records to which they
point.
The set method allows you to move through set connections in various directions. You
can move from the owner of a set instance to the first or last member of the set. From a
set member, you can move to the next or previous member record, or to the owner
record. During the navigation of sets, positions are established on a per-set basis: the
current owner and current member is indicated for each set type that has been used. If
defined, a system record can be used as the entry point into a database. When the
database is opened, the system record is the current owner of all sets owned by the
system record.
The sequential method allows you to find the first, last, next, or previous physical
instance of a given record type. RDM does not allow the programmer to insert records at
specific physical positions in a file. Their insertion is normally at the end of a file, but this
is not guaranteed if you are using the delete chain (see the entry on Option Settings in
section 5.2.4, "Option Settings," of Chapter 5). The sequential method is useful when you
are searching all records of a given type, but do not care about the order.
2-10
2.7
Database Concepts
2-11
Chapter 3
Operational Overview
3.1
Introduction
This chapter presents an overview of the basic operation of the RDM DBMS. Each
system component is identified and described, and the operational flow of an RDM C or
C++ program is given. A simple, introductory example is then developed illustrating the
basic use of the system. Your understanding of what each component does and how it
fits into the overall system is essential to your ability to use RDM effectively. (Note that
RDM does provide support for Unicode. For information about this feature, see Chapter
9.)
3.2
System Components
Figure 3-1 is a diagram of all of the RDM system components. Arrows indicate input and
output between the components and text files (for example, schema), specific file types in
the database (for example, key files), or the database in general.
ddlp
The Database Definition Language Processor, ddlp, is a utility that compiles a DDL
specification, called a schema, and produces the database dictionary. The dictionary contains database content and organization data that is used by the RDM runtime library
functions (see below).
A C or C++ header file (not shown in Figure 3-1) also is created by the ddlp. This file
contains constants and declarations associated with a specific database for use by the C
or C++ programs that access the database.
Operational Overview
3-1
Currency manipulations
B-Tree Functionality
Task functions
C Program
Runtime
Library
initdb
Schema
RDM
Database
db_QUERY
ddlp
prdbd
db_REVISE
Dictionary
dal
ida
dbimp
TAF
lockmgr
ASCII
lmclear
Log Files
console
dbexp
keypack
ASCII
Key Files
keydump
keybuild
dbcheck
dbclrlb
dbedit
Data Files
datdump
dchain
initdb
The initialization of the data and key files for a new database is performed by the database initialization utility, initdb.
3-2
lock manager
Management of system-wide multi-user lock information and database recovery control
is performed by the RDM lock manager. The lock manager runs as a background task
under the UNIX or QNX operating systems. Under Windows, the lock manager runs in a
separate window.
All multi-user RDM programs communicate with the lock manager to request
permission to access data and key files contained in an RDM database.
lmclear
lmclear is a utility used to clear a database user process that has failed or terminated
without having properly closed a database. The actual effect of the operation depends
upon the operating environment but, in general, lmclear informs the lock manager of the
failed condition (when the lock manager cannot get this information from the operating
system on its own) so that certain user tables maintained by the lock manager can be
properly cleared.
dbclrlb
The clear lock bit utility, dbclrlb, clears any set record lock bits in the specified data files.
It is usually considered to be an error for these advisory record instance locks to remain
set after the application terminates.
ida
The Interactive Database Access utility, ida, is a menu-driven database manipulation tool
that provides an easy-to-use interface for entering, modifying, and retrieving database
information through most of the RDM functions.
The ida utility is a useful tool for learning to use RDM as well as a tool for maintaining
existing databases.
dal
The Database Access Language, dal, is a simple, interpreted language that includes most
of the RDM user-level functions as its commands. With built-in variables and a looping
Operational Overview
3-3
dbcheck
The consistency of an RDM database can be checked using the dbcheck utility. This
utility checks to ensure that data and key files are consistent and that all set linkages are
correct. Thus, dbcheck is used to check for database corruption.
dbedit
The dbedit utility is a low-level database editor that allows a skilled developer to change
an RDM data file. It interprets the database dictionary, but does not perform a formal
database open. Editing of all portions of all records is allowed.
dbimp
Data from ASCII text files can be imported into an RDM database using the dbimp
utility. This utility is able to create, store, and form set connections involving one or
more record types from data in a standard ASCII format.
The dbimp utility is useful in setting up test data, transporting RDM data between
systems, importing data from another DBMS, and, in conjunction with dbexp,
performing ASCII file dumps and reloads of an RDM database.
dbexp
The database export utility, dbexp, is used to output the contents of an RDM database
into a standard ASCII format. The exported data can then be used by another DBMS.
You may also use dbexp with dbimp to transfer a database to a different environment or
for performing an ASCII file dump and reload.
keybuild
Utility keybuild is used to recreate key (index) files from the contents of data files. It is
useful for implementing DDL changes where non-key fields are changed to key fields or
key fields are changed to non-key fields. It can also be used to rebuild the key files after
a database corruption (for example, as reported by dbcheck).
keypack
The Key File Packing utility can be used to decrease the size of existing key files. This
utility is designed especially for large, static key files which will require very little or no
modification after they are packed. The keypack utility places as many keys as possible
into each B-tree node so that the key file is smaller and lookups are quicker. However,
modification of the key file after it has been packed becomes a very expensive operation.
3-4
prdbd
The print database dictionary utility, prdbd, prints a formatted report showing the contents of all the dictionary tables for a particular database. This detailed information on
the structure of the dictionary enables you to write database utilities or to perform database maintenance.
console
The console utility will display the lock manager tables. This information can be useful
to system administrators.
datdump
The data file dump utility, datdump, produces a formatted dump of the contents of a
data file. This report shows the data field contents and set linkage pointers for each
record occurrence on a data file. It is primarily for use in performing database maintenance and for learning how data files are organized.
keydump
Similar to datdump, the keydump utility produces a formatted dump of the contents of a
key file. It is primarily for use in performing database maintenance and for learning how
key files are organized.
db_QUERY
db_QUERY is an ad hoc query and basic report writing system based on SQL (IBMs
Structured Query Language). It provides conditional selection and sorting of
information contained in an RDM database. The data is accessed by a db_QUERY
program through a relational view of the network model. This unique approach is faster
than query processing in more traditional relational systems because it reduces the
amount of data that needs to be retrieved. db_QUERY is a C-linkable and royalty-free
library.
db_REVISE
Changes to the structure of an existing database design, such as the addition of new data
fields, record types, and sets, can easily be made using db_REVISE. A wide range of
database structure revisions are supported by db_REVISE, making it one of the most
powerful database restructuring tools available with any DBMS on micro, mini, or even
mainframe computers.
Operational Overview
3-5
3.3
Operational Flow
The basic operational flow for creating an RDM C or C++ application program is shown
in Figure 3-2.
DDL
Specs
ddlp
Dictionary
C Header
File
C Source
File
vista.h
C Compiler
Object
Code
Runtime
Library
Linker
Executable
Program
RDM
Database
3-6
The source code for your C or C++ application program, which includes the header file
created by ddlp and the standard RDM header file (vista.h), is then compiled, and the
resulting object code is linked with the RDM runtime library to create the executable
application program. The RDM library functions called from within the program
manipulate the database content as defined by the dictionary.
The following procedure summarizes the basic steps necessary to create an RDM
application.
1.
Design the database. That is, determine what data is to be stored in the database
and how it should be organized.
2.
3.
4.
5.
6.
Experiment with the database, using dal or ida to create and access some test
data. Refine your database design as necessary.
7.
8.
9.
Link the program with the RDM library to create your executable.
Operational Overview
3-7
3.4
Introductory Example
This section introduces the use of RDM through the development of a simple database
application. A description of the program requirements is given in the next section. The
database design is then explained followed by the program design. Finally, the program
code is described with particular attention paid to the RDM function calls.
3.4.1 Requirements
The purpose of the example project is to write a program that will maintain a database of
books, magazines (journals), and articles contained in a technical library. Information to
be stored in the database includes:
Author name: a text field containing the name of the author in "last, first, middle"
format.
Title: a text field containing the title of the book, magazine, or article.
Date published: a date field containing the copyright date of the publication.
Information type: an integer field containing a code that identifies whether the
information is a book, magazine, or article.
The stored information must be retrievable by either the identification code or author
name. If an author has more than one publication in the library, all of the publications
must be displayed.
User interaction will be performed by using simple text input and output. Five commands will be provided:
3-8
info_title
author
publisher
pub_date
info_type
Field id_code will be a unique key field. That is, it will be used to uniquely access a single record occurrence in the database.
The author field will be a non-unique key field. This will provide access to all information records associated with a particular author. This is because keys are maintained in
the index in sorted order and, thus, all duplicate keys will be grouped together.
The RDM DDL that implements this database design (assume it is contained in file
tims.ddl) is given below.
/* Technical Information Management System*/
database tims
{
data file "tims.d01" contains info;
key file "tims.k01" contains id_code;
key file "tims.k02" contains author;
record info
{
unique key char id_code[16];
char info_title[80];
key char author[32];
char publisher[32];
char pub_date[12];
int info_type;
}
}
The first line is a comment that describes the database. As in C, comments are specified
in DDL between "/*" and "*/" pairs. Although comments may be placed throughout the
database definition file, those comments before the keyword database will be copied into
the resulting header file.
Operational Overview
3-9
The database statement names the database. The name chosen here, tims, is an acronym
for "Technical Information Management System."
The DDL file (or schema) defines the database to be composed of one data file, tims.d01,
and two key files, tims.k01 (containing id_code keys) and tims.k02 (containing author
keys).
One record type named info is declared to contain each of the data fields. To accommodate the null byte that terminates strings in C, each character field is one character longer
than specified in the requirements . Note that id_code is identified as a unique key and
that author is identified as a key. (Duplicate keys are allowed unless the unique qualifier
is present).
The schema is compiled with the following command:
ddlp tims.ddl
: 312
0 errors detected
The ddlp reports the amount of dynamic memory that will be allocated by the RDM
runtime system for the dictionary.
3-10
Upon successful completion, the ddlp will have created two files. File tims.dbd contains
the database dictionary. File tims.h is the header file to be included in each C source file
using the tims database. The contents of tims.h are shown below.
#ifndef TIMS_H
#define TIMS_H
/* RDM Version: 5.0 */
/* Technical Information Management System*/
/* database tims record/key structure declarations */
struct info {
char id_code[16];
char info_title[80];
char author[32];
char publisher[32];
char pub_date[12];
int info_type;
};
/* record, field and set table entry definitions */
/* File Id Constants */
/* Record Name Constants */
#define INFO 10000
/* Field Name Constants */
#define ID_CODE 0L
#define INFO_TITLE 1L
#define AUTHOR 2L
#define PUBLISHER 3L
#define PUB_DATE 4L
#define INFO_TYPE 5L
/* Set Name Constants */
/* Field Sizes */
#define SIZEOF_ID_CODE 16
#define SIZEOF_INFO_TITLE 80
#define SIZEOF_AUTHOR 32
#define SIZEOF_PUBLISHER 32
#define SIZEOF_PUB_DATE 12
#define SIZEOF_INFO_TYPE 2
#endif
/* TIMS_H */
Operational Overview
3-11
This file contains a struct declaration for the info record. This struct is for use by the
application in declaring variables that contain occurrences of the info record from the
database. Also included in this file are constant definitions for the info record and for
each data field. These constants will be passed to the RDM C functions that require
them.
The next step is to initialize the database with the following command:
initdb tims
RDM will display the following text and, except in the very first initialization, prompt
you for confirmation:
RDM Version 5.0
Database Initialization Utility
Copyright (C) 1984-2000 Centura Corporation, All Rights Reserved
Initialization of database: tims
This will destroy contents of the following files:
tims.d01
tims.k01
tims.k02
continue? (y/n)
A "y" (yes) response will complete the initialization of the database files; the listed files
will be overwritten.
ent_info
del_info
by_id
pr_info
3-12
Figure 3-3 shows the function call structure for the program.
main
ent_info
del_info
get_info
by_id
by_author
pr_info
d_open
Open a database
d_close
Close a database
d_fillnew
d_keyfind
d_keynext
d_keyread
d_recread
d_delete
Operational Overview
3-13
A variable called irec is a globally declared info structure (declared in tims.h) to be used
by all of the functions for storing info record data.
#include <stdio.h>
#define LOCKCOMM_DATA
#include "vista.h"
#include "tims.h"
struct info irec;
/* Technical Information Management System */
main()
{
char cmd[20]; /* command entry string */
/* Use no lock manager */
d_lockcomm(LMC_NONE);
/* open the "tims" database */
d_open("tims","o");
for ( ; ; )
{
/* display command menu */
printf("\nTIMS Commands:\n");
printf("
1 - Enter technical information\n");
printf("
2 - Delete technical information\n");
printf("
3 - Find technical info by id_code\n");
printf("
4 - Find technical info by author\n");
printf("
q - Quit\n");
printf("enter command: ");
gets(cmd);
switch (cmd[0])
{
case 1: ent_info(); break;
case 2: del_info(); break;
case 3: by_id();
break;
case 4: by_author(); break;
case q: d_close();exit(0);
default :
printf("*** bad command--re-enter\n");
break;
}
}
}
The d_lockcomm call initializes the interface to the lock manager. RDM communicates
with the lock manager using several protocols. These protocols are specific to the
hardware or operating system platform on which your application is running. In this
example we are opening the database in single user mode, so we can specify no lock
manager communication type. For a full explanation of the various lock manager
communication options and issues, see the RDM Multi-User Guide.
3-14
The d_open call requires two arguments. The first argument is the name of the database
to be opened. The second argument (o) indicates that the database is to be opened in one
user mode (that is, only one user will access the database, and the lock manager is not
required). In the single-user version, the second argument is required to be o.
The main program displays the command menu, prompts for and gets a command character from the user, and calls the function associated with the command. If q is entered,
the database is closed (d_close) and execution is terminated (exit).
Function get_info gets the info record data from the user and stores it in variable irec.
Function pr_info displays to the user the contents of irec. These functions are given
below.
/* Fill irec with info data from user*/
int get_info()
{
char txt[40];
printf("author
: ");
if ((gets(irec.author) == NULL) || (irec.author[0] == \0))
return( EOF );
else
{
for ( ; ; )
{
printf("id_code : ");
gets(irec.id_code);
printf("title
: ");
gets(irec.info_title);
printf("publisher: ");
gets(irec.publisher);
printf("pub. date: ");
gets(irec.pub_date);
for ( ; ; )
{
printf("info type: ");
gets(txt);
sscanf(txt, "%d", &irec.info_type);
if ((irec.info_type >= 0) && (irec.info_type <= 2))
break;
printf("invalid - correct types are:\n");
printf("0=book, 1=magazine, 2=article\n");
}
printf("enter data (y/n)? ");
gets(txt);
if ((txt[0] == y) || (txt[0] == Y))
return( 0 );
}
}
}
Operational Overview
3-15
Note that get_info validates the info_type code to ensure that it is 0, 1, or 2. The function
returns EOF whenever gets returns NULL or the user enters only a <Return>.
/* Print technical information record
*/
pr_info()
{
printf("id code : %s\n", irec.id_code);
printf("author
: %s\n", irec.author);
printf("title
: %s\n", irec.info_title);
printf("publisher: %s\n", irec.publisher);
printf("pub date : %s\n", irec.pub_date);
printf("info type: ");
switch (irec.info_type)
{
case 0: printf("book\n");
break;
case 1: printf("magazine\n"); break;
case 2: printf("article\n"); break;
}
}
Function pr_info interprets the info_type code to display "book", "magazine", or "article."
Function ent_info (below) repeatedly calls get_info until EOF is returned and then calls
the RDM function d_fillnew to fill and store a new occurrence of the info record from
the contents of irec. The first argument passed to d_fillnew is record constant INFO
which is declared in file tims.h. If the id_code key value exists in the database, d_fillnew
returns status code S_DUPLICATE.
/* Enter technical information records into TIMS database
*/
ent_info()
{
char s[32]; /* generic string variable */
/* enter tech info into TIMS database */
while (get_info() != EOF)
{
/* create new tech. info record */
if (d_fillnew(INFO, &irec, CURR_DB) == S_DUPLICATE)
printf("duplicate id_code: %s\n", irec.id_code);
}
}
Function del_info deletes an info record occurrence. The record to be deleted is identified from a user-specified id_code. Function d_keyfind is used to locate the specified
id_code key and returns status S_NOTFOUND if the key is not on file. If the key is
found, d_recread is called to read the contents of the record into variable irec. Then
pr_info displays the contents of irec, and the user is prompted for confirmation of the
delete operation. If confirmed, d_delete is called to delete the record (and its keys).
3-16
Function by_id is actually similar to del_info in that an info record is located through a
d_keyfind of a user-specified id_code value. If found, the contents are read by
d_recread and are displayed by pr_info. Of course, no delete occurs since the display of
the record contents is all thats desired.
/* Find technical information by id_code*/
by_id()
{
char id[16];
printf("id_code: " );
gets(id);
/* search database for matching id_code key */
if (d_keyfind(ID_CODE, id, CURR_DB) == S_NOTFOUND)
printf("id_code %s not on file\n", id);
else
{
/* read found record and print contents */
d_recread(&irec, CURR_DB);
pr_info();
}
}
Locating info record occurrences by the authors name is somewhat more complex in that
there may be more than one record per author and all are to be displayed.
Operational Overview
3-17
Function by_author searches for the user-specified author name through the call to
d_keyfind. If found, the record contents are read into irec by d_recread and displayed
by pr_info. The user is prompted to press <Enter> when ready to continue. The call to
function d_keynext positions to the next higher author key, returning status S_OKAY if
there was indeed a next key (status S_NOTFOUND is returned when at the end of the
key file). Function d_keyread reads the value of the key into the author variable, which
is then compared (strcmp) with the author field in irec.
/* Find technical information by author
*/
by_author()
{
char author[32], reply[5];
int status;
printf("author: ");
gets(author);
/* search database for matching author key */
if (d_keyfind(AUTHOR, author, CURR_DB) == S_NOTFOUND)
printf("author %s not on file\n", author);
else
{
do
{
/* read found record */
d_recread(&irec, CURR_DB);
/* print record contents */
pr_info();
printf("--- press <enter> to continue");
gets(reply);
/* read next key on file--keys are always sorted */
status = d_keynext(AUTHOR, CURR_DB);
if (status == S_OKAY)
status =d_keyread(author, CURR_DB); /* read key
value */
/* continue while keys have same author name */
} while ((status == S_OKAY) &&
(strcmp(irec.author, author) == 0));
}
}
3-18
Assuming that the program was contained in file intro.c, and that you use the Microsoft
C compiler, the following command would compile the example program (in the large
memory model):
cl /c /AL /DMSC intro.c
To link the object code with the RDM library and produce the executable, use the
following command:
link intro,,,vistal;
Note that these commands all assume that you have the appropriate Microsoft C
environment set up for locating include files and libraries. In particular, files tims.h and
vista.h should be in either the current directory or in one of the directories listed by the
INCLUDE environment variable. The RDM runtime library should be located either in
the current directory or the LIB environment variables.
3.5
Directory Structure
Table 3-1 describes the directory structure for the RDM system.
The directory structure is designed so that RDM for Windows is installed in the
rdm_home directory.
The RDM directory structure includes directories for the components of db_QUERY and
db_REVISE. Those directories are also described in Table 3-1.
Operational Overview
3-19
dbedit
dbimp
examples
include
Header files.
lib
lockmgr
Source code for the lock manager and remote console utilities.
query
revise
rom
runtime
utility
3-20
Chapter 4
Database Design
4.1
Introduction
A database design is a description of the data that will be stored in a database and the
relationships that will exist within that data. The design of a particular database is
driven by the requirements of the application it will support, and is therefore a major
part of the total application design process.
Our purpose in this chapter is to provide you with the information necessary to design
RDM databases. If you carefully study this chapter and the example database design,
you should be able to design a workable RDM database, even if you have no database
design experience.
The chapter begins with a detailed description of RDMs Database Definition Language
(DDL). Each DDL statement is explained and illustrated through examples. The operation of the DDL processor is explained followed by a section on database design
considerations. The final section builds on the tims database example introduced in
Chapter 3 by expanding the requirements and describing how the database design
supports those requirements.
4.2
Database Design
4-1
database ckngacct[512]
{
timestamp records budget;
data file datfile = "chkg.dat" contains budget, check;
key file[1024] keyfile1 = "chkg.k01" contains code;
key file[2048] keyfile2 = "chkg.k02" contains check_no, check_date;
record budget
{
unique key char code[6];
char cat_desc[48];
float allocation;
float balance;
}
record check
{
unique key int check_no;
key int check_date;
char paid_to[48];
float amount;
}
set transactions
{
order last;
owner budget;
member check;
}
}
The DDL specification is stored in a text file and is created using your usual text editor.
Input is free form with comments specified, as in C, between /* */ pairs. Comments do
not nest. This can be an ASCII text file, or, on a Unicode-supported platform, a Unicode
file. For information about RDM support of Unicode, see Chapter 9.
Identifiers are used to name the database, files, records, fields, and sets. They are
formed, as in C, from any combination of letters, digits, and underscores (_) beginning
with a letter.
File, record, and set statements may be interspersed provided that:
Data and key file statements are specified ahead of the declarations of the records
and key fields they contain.
The declarations of set owner and member record types are specified ahead of their
respective set declarations.
4-2
optionally, produce a record, field, and set name cross-reference listing to help you locate
names contained in a long DDL specification (see section 4.3, "DDL Processor
Operation").
For the checking account example, the name of the database is ckngacct and it has a
default database page size of 512 bytes. The name of the dictionary file created by ddlp
is ckngacct.dbd. The name of the C header file created by ddlp is ckngacct.h.
database acctsrec { ... }
Database Design
4-3
The above is a possible database statement for an accounts receivable database. Since no
page size parameter is specified the default database page size is 1024 bytes. The
dictionary will be stored in file acctsrec.dbd and the C header file will be named
acctsrec.h.
In the checking account database, only the budget record type is to be timestamped. This
allows an appraisal to see if another user has modified or deleted a particular budget
record before a check is written to the database, which updates the balance for that
budget category.
timestamp records;
timestamp sets;
The database for the above example requires that all record and set types be timestamped.
4-4
Database Design
4-5
A special system-defined record named system may be listed as the first record type in
the contains clause of one (and only one) of the data files. Only one occurrence of the
system record exists in the database. It is used as the owner of any number of sets to
provide the means for records to be connected to the top or root of the network. When
the database is opened, the current record and the current owners of all system-owned
sets are initialized to the system record.
All key fields defined in the DDL must be contained in a key file. All occurrences of the
key fields listed in the contains clause will be stored in the file. Occurrences of a given
key type can only be stored in a single file. Each page in the key file consists of one or
more fixed-length key slots. The size of the key slot is based upon the size of the largest
key field contained in the file. Smaller key fields will occupy the same slots, thus leaving
unused space. You can define all key fields to be contained in one file or you can have a
separate key file for each individual key field. The choice is yours.
Section 4.4.2, "Physical Design Considerations," provides some guidelines to help you
determine the best file organization for your particular application.
Examples:
data file datfile = "chkg.dat" contains budget, check;
key file[1024] keyfile1 = "chkg.k01" contains code;
key file[2048] keyfile2 = "chkg.k02" contains check_no;
The checking account database consists of one data file and two key files. The data file
identified as datfile contains occurrences of both the budget and check record types in
the physical file named chkg.dat. From the database statement the page size for datfile
is 512 bytes. The key file identified as keyfile1 has a page size of 1024 bytes and contains
the index for key field code in file chkg.k01. Key file keyfile2 has a page size of 2048
bytes and contains the index for key field check_no in file chkg.k02.
data file "/client/master.dat" contains system, master;
The above file name specified in this data file statement includes the fully qualified path
for file master.dat located in the client directory. In addition to the occurrences of the
master record, this file will contain the system record.
key file "invnt.k01" contains stock.id_code;
key file "invnt.k02" contains bkorder.id_code;
The above example shows duplicate key names that are qualified by the record type in
which they are defined. When duplicate field names are used, it is necessary to use the
-d option with the ddlp command (see section 4.3, "DDL Processor Operation").
4-6
Database Design
4-7
Example:
record check {
unique key int check_no;
int check_date;
char paid_to[49];
float amount;
}
The check record type in the checking account database contains two integer fields (one
of which is a key), a character string field, and a floating point field. File chkgacct.h will
contain the following definitions associated with the check record type:
struct check {
int check_no;
key int check_date;
char paid_to[49];
float amount;
};
#define CHECK 10001
Record trans below contains two compound key field declarations. (For more information, see "Use of Keys" in section 4.4.1, "Logical Design Considerations."
record trans {
unique key int checkno;
int trdate;
char vendid[8];
long amount;
compound key tr_key {
trdate descending;
vendid ascending;
}
compound key ven_chks {
vendid; checkno;
}
}
Below are two record types and a set that might be defined for a multi-user data entry,
forms management system. Note that both records are declared to be static, since during
normal operation of an application using the forms manager, form and field records are
not modified.
4-8
Database Design
4-9
The fldname is an identifier that names the particular data field. Field names may not
duplicate record or set names, nor by default other field names.
The syntax for a data field statement is similar to, although not as general as, a C data
declaration. In fact, most of the basic data types in C are directly supported in the DDL.
Arrays of any type are available, where dim specifies the size of a given array dimension.
One-dimensional character arrays are treated by the RDM runtime functions as C strings.
Thus, these fields should always be terminated by a null byte, and the length as specified
in the field declaration should include the null byte. If a one-dimensional character array
is needed that is not intended to be treated like a string (for example, a byte array), it
should be declared as a two-dimensional array where the second dimension is one.
A data field that is also to be used as a key to the record has the key attribute. Key field
values are stored on the key file in the natural order based on the data type. If only
unique keys are allowed then the field should be qualified as a unique key field. The
maximum length of a key field is 246 bytes. Fields defined as a unique key must contain
a value that does not already exist on the key file at the time its associated record is
entered into the database. If a record is entered (modified) with a duplicate value in a
unique key field, the status code S_DUPLICATE is returned and the record is not
entered (or modified).
The optional attribute indicates that the data field is an optional key. Optional key
values are not inserted into the key file until the application program calls function
d_keystore. Optional keys can be manually deleted using function d_keydel. When an
optional key is modified (through a d_recwrite or d_crwrite call), the key file will be
updated only if the current value exists in the key file.
Fields declared as struct cannot be nested. Sub-fields of an arrayed struct field cannot be
defined as key fields nor accessed individually.
Data fields of type DB_ADDR contain the database addresses of specific record occurrences in the database. Database addresses can be directly accessed using the currency
table access functions described in section 5.3, "Currency Tables." This allows you to
maintain your own record linkages directly, if desired.
The optional om_field should only be used when ddlp will be used to generate C++
classes for ROM. The directref option must be used on a DB_ADDR field. With this
option, methods are created in the resulting class to allow ROM to have direct access
from this record to the record being referenced through this field. The relatedto option is
designed to allow ROM to know about relational links. The field in the record that is
being related to this field must be a key field. This allows ROM to create methods to
easily navigate through this relational link.
4-10
Data fields will be aligned within the record in order to match the struct field alignment
rules followed by your particular compiler and computer. A ddlp option can be used to
disable this alignment if desired (see section 4.3, "DDL Processor Operation").
Examples:
unique key char code[6];
Field code is a character string field of six characters long (five characters plus one null
terminator) that is defined as a unique key.
float balance;
Field balance is a floating point field which, in the checking account database, contains a
monetary value.
key int check_date;
Key field check_date is used to store a date in the application-defined Julian format (for
example, number of elapsed days since January 1, 1900). Its values are stored on the key
file in integer order. Thus, check records can be retrieved in check date order through
use of the key retrieval functions (see section 5.4.1, "Data Retrieval Using Keys").
struct {
double imag;
double real;
} complex[3];
This field statement defines a structure array field, complex, which stores an array of
three complex numbers composed of an imaginary part and a real part.
key long coordinates[3];
Coordinates is an array of three long type variables, and is also a key. It may be used to
locate an object on a large three-dimensional grid.
int bitmap;
Field bitmap is used to store a bitmap of attribute flags, which are tested using binary
operators and masks. For example, assuming this field was declared in record type rec,
rec.bitmap & 0x0001 is non-zero if the low order bit is set. (Note that RDM does not
directly support C bit fields.)
char byte_array[16][1];
Database Design
4-11
Field ptr_array is an array of type DB_ADDR. It is used to store an array of the database
addresses of record occurrences that are related to the record type in which ptr_array is
defined. Use of DB_ADDR fields provides unlimited data organization possibilities to
the programmer. However, these alternatives should only be used in those rare instances
when the standard capabilities provided by keys and sets are insufficient for a particular
requirement.
optional key struct {
char last_name[21];
char first_name[21];
char initial[2];
} name;
Name is a struct field to contain person names and is composed of three string fields for
the last and first names and the middle initial. Last_name is the first field specified since,
because name is a key field, the last name can be used in a partial key search to find, for
example, all the Smiths on file. Thus, the order of the fields in a keyed struct field
specifies the major and minor sort sequences for the key on the key file.
This field is defined as an optional key. Optional keys are often used to defer the overhead associated with storing keys to a time when the system is less busy. For example, it
might be that the record which contains name needs to be stored as rapidly as possible
during the day. At night a batch program can be run to create the optional keys.
4-12
record. By using compound keys, you can have a field appear in multiple keys within a
record, without needing to duplicate the fields value in the data file.
The compound key specifications must follow all other field statements in a record declaration. The order in which the sub-fields are specified determines the major and minor
sort sequences. The fldname must be the name of a field that is defined in the record and
is not defined as a struct. If the optional qualifier is specified, the key will only be stored
when d_keystore is called. Otherwise, the key is created when the record is created. All
of the key functions that apply to normal key fields also apply to compound keys.
ddlp will create in the dbname.h or dbname.hpp file a struct (or class) declaration
named keyname for each compound key defined in the schema, similar to the struct declarations associated with records. These can be used in conjunction with the key manipulation functions of the RDM runtime library.
Because of the nature of compound keys, records containing them can only be created
using function d_fillnew (not d_makenew).
Examples:
compound key tr_key {
trdate descending;
vendid ascending;
}
compound key ven_chks {
vendid;
checkno;
}
Assume that a record type called trans (transaction) contains two compound key definitions. Key tr_key is composed of fields trdate and vendid. Scanning through transaction
records by tr_key would produce a sorted list in descending transaction date order, and
ascending vendor id order within each date. Key ven_chks consists of two fields: vendid
and checkno. Scanning through transaction records by ven_chks would give a sorted
list in ascending vendor id order, and in ascending check number order within each
vendor.
Database Design
4-13
Description:
Set declarations define explicit, one-to-many relationships between record types. Sets are
implemented as a linked list of member record instances connected to a single instance of
an owner record, which serves as the root or head of the list (see "Set and Member
Pointers" in section 14.2.2, "Data File Organization"). The order in which member records
are inserted into this list is specified in the set order clause.
Possible set orderings are defined as follows:
first
last
ascending
descending
next
The by part of the member clause is only supplied when ascending or descending order
is specified. For sorted sets having more than one member record type, the sort field(s)
of those record types should correspondingly be of the same type and length and be
listed on the by clause in the same order.
When the sort field values of new member records of a sorted set duplicate existing
members, the new members are added in front of the members with matching values.
The set owner may be specified as system. Use of the system record is not required. If
one is used, do not declare it in a DDL record statement. ddlp automatically creates the
system record when it is specified in a data file statement. There is only one occurrence
of the system record in the database. It is used as the owner of any number of sets
providing the means whereby records can be connected to the top or root of the network.
When a database is opened, the current record and the current owner of all
system-owned sets are initialized to the system record (see section 5.3, "Currency Tables,"
for a discussion of currency).
Connecting member records to sets of order first, last, or next is faster than connecting to
sets of order ascending or descending. This is because the list of member records
associated with ascending or descending sets must be scanned each time a new member
is connected, in order to find the proper insertion point. Thus, a connection to an ascend-
4-14
ing or descending set with a large number of members can be relatively slow. If you
need a large sorted set, explore alternative approaches using keys or reverse ordering to
maintain the ordering.
Note: RDM does not implement ascending or descending sets through an index. As
with all sets, they are implemented as a linked list (or chained) structure. RDM does
provide a keyed record access (through a B-tree index) but it is totally distinct from sets.
A record can both have keys and be an owner and/or member of sets, but sets do not use
keys and keys do not use sets. Thus, sort fields of ascending or descending sets do not
need to be declared as key fields.
The keywords bitmap, blob, and varilen should be used only when ddlp is generating
C++ output for ROM. Each provides different ROM capabilities when the set is being
used to store bitmaps, blobs and variable length text. Refer to the ROM documentation
for more details.
Examples:
set transactions {
order last;
owner budget;
member check;
}
Transactions is a set between the budget record and the check record in the checking
account database. Each check written is to be applied to a particular budget account.
Each budget account has a set of checks that have been written against it. As a new check
is written and entered into the database, it is connected to the transactions set instance,
where the appropriate budget record has been identified as the current owner of the set.
The new check record will be connected to the end of the set. Thus (assuming checks are
written in order), they will be stored in check number order (and probably date order as
well).
To ensure that checks are stored in date order, the set specification could be modified as
follows:
set transactions {
order ascending;
owner budget;
member check by check_date;
}
Here the order has been changed to ascending and the member clause now includes the
by part to indicate that the set is to be sorted on the check_date field of the check record.
Again, the connect operation will be slower than if the order is last. However, if the sort
field is known to usually force the insertion to be at the end of the set instance, the
ordering of the set could be reversed so that the set remained sorted and so that the
insertion was usually made quickly at the front of the list.
Database Design
4-15
set comment {
order first;
owner note;
member project;
member task;
member work_day;
}
The comment set illustrates a use of multiple member sets to reduce unnecessary space
overhead. Each of the project, task, and work_day record types in a project management
database can have an optional comment associated with it in the note record. In this example, an occurrence of project, task, or work_day can be associated with only a single
occurrence of note. Thus each set instance is strictly one-to-one. Use of a single set is
preferred over three separate sets because it will use less space for the set overhead (one
set pointer is needed instead of three). See section 4.4.2, "Physical Design
Considerations," for more information.
4.3
4-16
If the -x option is given, a cross-reference listing of the records, fields, and set identifiers
is displayed in stdout. An example report is shown below for the checking account
schema.
RDM Version 5.0
DDL X-Ref Listing of File: ckngacct.ddl
Wed May 04 16:53:32 1999
allocationfield
12
amount
field
balance
field
budget
record
cat_desc
field
check
record
check_datefield
7 17
check_no
field
code
field
paid_to
field
transactions
set
19
13
3
23
11
5
7
15 24
16
6 10
8
21
The names are listed in alphabetical order, with the associated type and the line numbers
in the DDL file where the name is referenced.
The -b option is used to instruct ddlp not to perform any data field alignment. Normally,
the offset to the start of each data field in a record is aligned according to the default
struct field alignment rules enforced by your particular C compiler. Some compilers,
however, provide an option to pack struct fields. If you use this option when you
compile any C modules that use the struct declarations created by ddlp, you must also
compile the DDL using the -b option.
The -c option is used to maintain alignment compatibility with RDM version 2.x
databases. If -c is specified, and the number of bytes per word is other than 2 bytes, then
you must use the -ln option to inform ddlp of the number, n, of types per word for your
machine. For example, VAX and M68000 processor users should always compile with
-l4.
With the -d option, ddlp allows duplicate field names, such that field types within different record types may have the same name. The record structures created by ddlp will
contain the name as specified in the schema. The constant definitions for the fields will
be a concatenation of the record type and field type, separated by an underscore. For
example:
Database Design
4-17
record ticket {
.
.
float unit_cost;
}
record invoice {
.
float unit_cost
}
If duplicate field names are used with key fields, then each reference to the key field
name in the key file statement must have a prefix showing the record type that contains
the field. The syntax is recname.fldname.
The -n option instructs ddlp to omit writing the ASCII record, set, and field names to the
dictionary file. This creates a smaller dictionary file, but makes names unavailable to
utilities such as ida, dbimp, and db_REVISE, and programs made with db_QUERY.
ddlp creates C struct (C++ class) declarations for each record type and compound key
field defined in the DDL and stored with file id, record, field, and set constants in a
header file also named from the dbname of the database statement forming dbname.h
(dbname.hpp). By default, case is preserved on all of the names used. The -s- option
may be used to instruct ddlp to convert all of the names to lowercase to be compatible
with previous versions.
The ddlp can process constants, #defines, predefined structures, and typedefs. All of
these keywords must be used before ddlp recognizes the keyword database. Everything
before the keyword database is copied to the resulting header file, so the application will
not need to redefine anything.
As an example, the contents of file ckngacct.h, which was created when the checking
account schema (as given in section 4.2.1, "DDL Basics") was processed by ddlp, is shown
below.
4-18
#ifndef CKNGACCT_H
#define CKNGACCT_H
/* RDM Version 5.0 */
/* database ckngacct record/key structure declarations */
struct budget {
char code[6];
char cat_desc[48];
float allocation;
float balance;
};
struct check {
int check_no;
int check_date;
char paid_to[48];
float amount;
};
/* record, field and set table entry definitions */
/* File
#define
#define
#define
Id Constants */
DATFILE 0
KEYFILE1 1
KEYFILE2 2
/* CKNGACCT_H */
The #define constants are passed to runtime library functions to identify the particular
file, record, field, or set type involved in the operation. The actual values represent
Database Design
4-19
entries into the tables that make up the database dictionary. The constants also have
some additional control information encoded, as follows:
Record name constants consist of a record number (record 0 is the first record defined
in the DDL, record 1 is the second, and so on) plus 10000.
Set name constants consist of a set number (numbered sequentially, as are records)
plus 20000. This information is used by RDM runtime functions to distinguish
between record and set constants.
Field name constants are formed using the following formula:
(record number * 1000) + number of field within record
This can simplify the work involved in adding a new field to a record. In pre-3.00
versions of RDM, the field constant was simply the index of its dictionary entry.
However, if a new field was added, all source modules had to be recompiled because the
new field changed the values of the field constants. With the new technique, if a new
field is only added to the end of a record, only those modules that need to reference the
new field need to be recompiled. Note that field constants are long integers, whereas file,
record, and set constants are standard integers.
The SIZEOF_?????? constants are added in the header file as a convenience of the user. If
they are not needed, or are causing problems with large databases, they can be removed
with a -z option.
The database header file should be #included in every C source module that needs to
access the database.
4-20
If you have existing data that needs to be preserved, you can either use db_REVISE to
convert from the older ddlp to the newer version, or you can use the dbexp and dbimp
utilities.
You also should check the alignment of your data structures any time you change
compilers. If the alignment is different, you may need to adjust your alignment for the
compiler and the database.
Nested Structures
Be very careful about using nested structures in records. The alignment of nested structures is very compiler-dependent. Although RDM compensates for this dependence,
ddlp may not always be able to produce the correct alignment of nested structures.
While numeric and character data can be included in the same record, we recommend
that numeric data and character data not be mixed together. You should put the
doubles, longs, ints, and shorts first, with the chars last. This will create the smallest
possible records without artificially packing the structures and avoid most alignment
problems.
If you plan to use nested structures, we recommend that they start on word boundaries
(that is, with numeric data). Starting the structures on eight-byte boundaries may be
necessary on some machines when the structures contain double data types.
-abcc
ddlp can also use the ddlp environment variable to set word alignment. To set ddlp to
use word alignment for Microsoft C, use either the -amsc switch or use set ddlp=msc to
set the ddlp environment variable.
Applications should use -b for byte alignment when packing structures.
4.4
Database Design
4-21
This section presents database design considerations as they pertain to RDM. These
design considerations are separated into logical design and physical design issues.
Logical design (as defined here) involves those aspects of the database organization that
directly affect the manner in which the C applications have to access and manipulate the
stored information. Physical design addresses the organization of the database into data
and key files and (except for file renaming and initialization) is transparent to the
application program.
Use of Keys
General Key Usage
Key fields are basically used:
What are the possibilities for locating a specific record occurrence among many? One
could read and inspect each record occurrence in the order they were stored in the file,
until the desired record is found. This would take some time if there are many occurrences. On average, half of the records on file would have to be inspected to find a specific record, and all would have to be inspected to determine that the record was not on
file.
Alternatively, sets could be navigated through the network structure to locate the desired
record (see section 4.5.3, "Database Design"). This would generally mean that fewer
records would have to be read, but the process still would be too slow, depending on the
application requirements.
Because of the efficiency of the B-tree indexing method, locating a record through a key
will involve at most only a few disk read operations (usually three or four). Thus, it is an
ideal way to locate specific record occurrences rapidly.
In RDM, any field defined in a record can be a key. Thus there are a wide variety of
potential ways to access a particular record. Fields can be defined as unique keys, to
prevent the entry of records containing duplicate key values. When key fields are not
defined as unique, records with duplicate keys are allowed to reside in the database. For
example, zip code may be a key in a person record containing an address.
4-22
In the checking account database, check_no was defined as a unique key to allow the
program to retrieve the check record for a particular check number. Other examples of
data fields that would be good candidates for keys are:
employee number
social security number
vehicle identification number
inventory item number
budget code
serial number
personal name
Many different kinds of fields containing coded values are best implemented using a key.
If new codes are needed, they can be added to the database without recompiling. For
example, consider the following record declarations:
/* vehicle make validation table */
record vehicle
{
unique key vma_code[7]; /* vehicle make code */
char vma_desc[25];
/* vehicle make description */
}
/* vehicle fleet record */
record fleet {
unique key char vin[25];
/* vehicle id number */
char vma[5];
/* vehicle make */
char vmo[5];
/* vehicle model */
char vyr[3];
/* vehicle year */
int lsd;
/* last svc. date */
}
When a new fleet record is entered, the application program can easily validate that the
vma field contains a correct code by doing a key find operation on its contents (specifics
of how to do this are explained in section 5.4.1, "Data Retrieval Using Keys").
The check_date field in the checking account database example was not a unique key.
This provided the ability to write multiple checks with a common date and to quickly
retrieve all checks written on a specific date or within a particular range of dates.
Key fields can also be used for rapid pattern-matching type searches. Many more keys
than records can be retrieved in a single disk read. Use of a key field for certain search
requirements will result in much faster processing, especially when all occurrences need
to be checked by the search operation.
Database Design
4-23
Meaning
Other
The reason for maintaining m.o. records in the database is, of course, to allow searches
for a match of a selection of the fields. The key for the search would be a 25-element byte
array in which each elements value would be a numeric code, with zero meaning "not
supplied."
record mo {
key char mo_data[25][1];
}
The fields in the key would be ordered with the highest priority search field first and the
lowest priority search field last. The highest priority search field is the one most often
used in the searches. By listing it first, you can reduce the number of keys to be scanned
in the maximum number of cases. If the first field is not used in the search, all keys must
be scanned.
Compound Key Usage
In general, compound keys are used when one of the following conditions exists:
A data field needs to participate in more than one key in the record.
A key is needed that contains multiple fields, where a mix of ascending and
descending order is required among the sort fields.
Suppose a record is to be searched based on the contents of two fields, where search
values for either field are not always available. If a struct key field were used and the
first field in the key was not available, all keys would need to be scanned. The use of two
compound keys would solve the problem.
4-24
record combo_search {
int field1;
int field2;
compound key f1_1st {
field1; field2;
}
compound key f2_1st {
field2; field1;
}
}
If only field1 data were available for the search, then key f1_1st would be scanned. If
only field2 data were supplied, then key f2_1st would be scanned. If both were supplied, by convention, f1_1st would be scanned.
Compound keys allow sort fields to be individually sorted in either ascending or descending order. As an example, suppose in the checking account database we wanted to
be able to list the checks sorted by the paid_to field, and, within that, in check number
order with the most recent checks listed first. The easiest way to do this is to use
db_QUERY, but for the purpose here we will use a compound key, as follows:
record check
{
unique key int check_no;
key int check_date;
char paid_to[48];
float amount;
compound key pay_list
{
paid_to ascending;
check_no descending;
}
}
4-25
Use of Sets
Set relationships form the basis of the network database model. The basic use of sets in
forming one-to-many relationships was introduced in Chapter 2, "Database Concepts,"
and illustrated in the checking account database example. This section will expand the
use of sets by showing how they can be used to implement many-to-many relationships
and variable-length records.
Many-to-Many Relationships
Many-to-many set relationships are best explained through an example. A typical
example is students and classes in college. Each class has many students and each
student takes many classes. Thus, there is a many-to-many relationship between classes
and students.
Sets, however, only implement one-to-many relationships. On the surface, it might
appear that the following incorrect implementation using two sets would work.
record class {
unique key char class_id[6];
... other fields
}
record student {
key char name[36];
... other fields
}
set my_students {
order last;
owner class;
member student;
}
set my_classes {
order last;
owner student;
member class;
}
4-26
class
my_students
my_classes
student
Students: Smith
Jones
Kelly
Carlson
A problem occurs when the my_classes set instances for these students is examined.
Each student in CS101 must have that same class as a member of his my_classes set
instance. This is not possible, however, since CS101 can only be connected to a single
owner in the my_classes set.
The correct technique for implementing many-to-many relationships does indeed utilize
two sets but through the use of an intersection record type, as follows:
Database Design
4-27
record class {
unique key char class_id[6];
... other fields
}
record student {
key char name[36];
... other fields
}
record intersect {
}
set my_students {
order last;
owner class;
member intersect;
}
set my_classes {
order last;
owner student;
member intersect;
}
Figure 4-2 shows the schema diagram that corresponds to the above DDL.
student
class
my_classes
my_students
intersect
4-28
Variable-length Records
Only fixed-length records are allowed in RDM. In systems where variable-length records
are supported, they are often implemented by storing the variable-length information in
a linked list of fixed-length segments. This technique can be implemented by the RDM
application developer through sets.
In the simplest example, a note of arbitrary length is associated with a record such as a
customer record. If the note is stored as a field in the customer record, then an arbitrary
limit on the length of the note text would need to be imposed. Much of the space taken
up by the field would never be used. If, however, a separate record type were defined to
store a single line of text, a set relationship between the customer and note text line
would allow any number of text lines to be connected to a particular customer. The
schema diagram in Figure 4-3 shows this relationship.
customer
cust_notes
text
Fig. 4-3. Example Variable-Length Text Implementation
The corresponding DDL statements are shown below.
record customer {
unique key char cust_id[7];
char company[21];
... other fields
}
record text {
char line[80];
}
set cust_notes {
order last;
owner customer;
member text;
}
Database Design
4-29
Use of multiple member sets can save even more space. Consider the alternative schema
diagram in Figure 4-4.
customer
cust_notes
text30
text55
text80
Multiple member sets allow any occurrences of the record types that are defined as valid
members to exist in the same set instance. Thus, in this example, a customer record may
have text30, text55, and text80 record occurrences connected through the cust_notes set.
This allows the program to choose the record type that best fits the amount of text
actually entered for a given line of text. Note, however, that each of the text record types
4-30
should be contained in a separate data file, or no real space savings will occur (see section
4.4.2, "Physical Design Considerations").
The C code that manages the cust_notes set connections is described in Chapter 5,
"Database Manipulation."
Sometimes temporary application control information needs to be manipulated in sufficient quantities to benefit from the use of database operations on that data. This information can be stored in a temporary working database, which is initialized when the
program begins and deleted when the program terminates.
db_REVISE uses just such a working database to control the restructuring of an old database into a new one. Thus, in db_REVISE three databases are opened and accessed at the
same time. db_QUERY uses a working database to perform sorting.
An accounting package is a good example of a modular database application. Most such
packages provide separate modules for various accounting functions. Companies only
purchase the modules they need. A typical package would include these modules :
General Ledger
Accounts Receivable
Accounts Payable
Payroll
Inventory
Sales Orders
Almost all the modules require the general ledger module. The amount of shared information between the other modules depends on the application.
Database Design
4-31
When designing an application that will use multiple databases, keep in mind that
inter-database relationships can exist, but can only be implemented through keys or
through the use of DB_ADDR fields containing the database addresses of records in
another database. It is not possible to have a set with an owner defined in one database
and a member defined in another. However, an account number stored in the accounts
receivable database can be used to find the associated account record in the general
ledger.
Optional key bit maps (one byte for every eight optional keys defined in the record)
One set pointer (12 or 16 bytes) for each set for which record type is defined as an
owner. A set pointer for a timestamped set uses 16 bytes, otherwise only 12 bytes are
used
One member pointer (12 bytes) for each set for which record type is defined as a
member
4-32
The information to be considered in database design is the space required for set and
member pointers, as this is determined from the DDL.
Chapter 14, "File Formats and Dictionary Tables," describes in detail the structure of
RDM files, records, and keys.
Place records and keys in separate files for better multi-user concurrency.
The chances of multiple users locking the same file is increased when several record
or key types are contained in the same file. Placing each key and record type in its
own file will minimize access conflicts.
Place owner and member record types in the same file for improved set access
performance.
If new owner and member record occurrences are stored and connected in the same
transaction and if they are located on the same file the likelihood of their being
placed on the same database page is increased. This could yield better set access
performance.
Minimize the number of separate key files to improve virtual memory performance.
Virtual memory is a technique in which frequently accessed database pages are kept
in memory, thus reducing the amount of actual disk input and output required.
Using fewer key files increases the probability that needed index pages will be in the
cache (that is, the virtual memory buffers).
Experiment with the size of the pages to change the number of slots per page.
Once the application is built, there will be only a fixed amount of memory left over
that can be used for the RDM cache. Some applications get better performance from
a smaller number of larger-sized pages that contain more slots per page. Other applications get better performance from a larger number of smaller-sized pages that
contain fewer slots per page.
Database Design
4-33
: 1
: 0
RECORD: CHECK
Id
: 1
File
: chkg.dat [0]
Total set pointers
Total member pointers
Offset to data
Size of record
Unused slot space
: 0
: 1
4-34
: 26
: 88
: 0
: 18
: 74
: 14
The summary for each data and key file includes the following:
File name
File id number
The information most significant to database design is the number of slots per page and
the amount of unused page space in a data file. The unused page space occurs because
the slot size multiplied by the number of slots per page does not always exactly equal the
size of the database page. Much of this unused space can be reserved by adding an extra
(unused) data field to the largest record on the file. The length of the field is computed as
follows:
length = (unused page space) / (slots per page)
The unused space is contained in a record so that if additional data needs to be stored in
that record, space will be available. If the reserved space is large enough, the new field
can be added without restructuring the database.
The record summary contains the following information.
Record name
Record number
The design-related information in this report is the unused slot space. This space can
easily be converted into a usable form by adding an extra field the length of the unused
slot space to the record. Then, as explained above, the space is available for additional
data to be stored in the record at a later date, without requiring a restructure of the database.
Database Design
4-35
Also important in calculating the size of the records is the ordering and size of the data
types used in the record. Most systems require numeric data to start on word boundaries. This means that if the field preceding a numeric field does not end on a word
boundary, unseen and unusable padding is inserted to force the numeric data to the
word boundary. When character and numeric data are not carefully mixed, wasted
space can be generated.
4.5
4.5.1 Introduction
The example to be presented in this section is an elaboration of the tims database introduced in Chapter 3. It will be used in examples throughout the remainder of this
document. A solid understanding of this example design is necessary.
The requirements for the tims application are given first, followed by a description of the
schema with explanations of how the design will be used to satisfy the stated
requirements.
4-36
4.5.2 Requirements
The system is to be used to maintain a database of technical information contained in
books, technical journals or magazines, and articles. In the following discussion, a single
book, journal issue, or article will be generally referred to as an info item.
The following data is to be stored for each book, journal, or article:
authors name
information id code
title
publisher
date published
abstract
topical key words
The id code will be a unique Dewey-Decimal library code assigned by the user. The
abstract will be a brief description (up to several paragraphs) of the info item. Each info
item may have several key words associated with it that identify topics discussed in it.
Functions are to be provided to allow info item entry and deletion.
The info item data is to be retrieved as follows:
By author name, where all info items for a given author are reported
By key word, where all info items for a given key word are reported
The ability to keep track of loaned books and magazines is also to be provided, where the
borrowers name, the date borrowed, and the date returned are stored for each item
loaned. A loan history is to be maintained for each info item. In addition, the ability to
report all unreturned info items is to be provided.
Database Design
4-37
system
author_list
loan_history
author
has_published
articles
key_word
info
loaned_books
info_to_key
key_to_info
abstract
intersect
text
borrower
4-38
be faster if we used the author name as a key field and did not use a set. Here (mainly
for instructional purposes), the assumption is that the system is for a small, personal
library.
A simple variable-length text structure is used for storing the abstract. A record type
called text is defined that stores a text string of up to 80 characters, including a sentinel
null byte. A set called abstract with order last is defined with info as owner and text as
member, forming a one-to-many set between an info record and each line of abstract text.
The relationship between key words and item info records is many-to-many. A key
word is stored in a record type named key_word. The key word is a string field that is
keyed to allow rapid retrieval of individual key word occurrences and to allow alphabetized key word perusal. The many-to-many relationship is implemented through the
use of two sets, as described in section 4.4.1, "Logical Design Considerations." Key_word
records and info records are connected to an intersection record called intersect. Set
key_to_info is used to find the info records corresponding to a particular key word. Set
info_to_key is used to find the key words associated with a given info record. The
intersect record has one field to hold a copy of the info type from its info owner through
the info_to_key set. By eliminating an extra disk read of the info record for non-books,
this facilitates the kind of key word searches where, for example, youre only interested
in finding the books covering a specific topic. Redundant data is sometimes incorporated
into a database design in order to improve data access performance.
A record type named borrower will contain the name of the borrower, the date loaned,
and the date returned. The borrowers name will be a key field, in order to be able to
quickly find all of the items borrowed by a particular person. Dates will be stored as a
long integer of the form YYMMDD (for example, 870709 is July 9, 1987). A date of zero
indicates that the loaned item has not yet been returned. When an item is loaned, a new
borrower record is created and is connected to two sets. A set called loaned_books
connects the borrower record to the info record for the loaned item. These records will
normally remain members of this set even after the item is returned, to maintain a loan
history for each item in the library. The borrower record is also connected to a set called
loan_history, which is owned by the system record. This set is scanned when a list of all
unreturned books is desired. Both sets are in last order so the records will be connected
in chronological order (without having to specify ascending order by date loaned).
One final set has been included. The set named article_list has info records participating
as both owner and member of the same set (which is legal in RDM). Here, the set is
intended to connect article info records to the info record of the journal or magazine in
which it is published.
The RDM DDL that implements the tims database design is presented on the next page.
Two data files and two key files have been defined. Data file tims.d01 contains the
system record (of which there is only one occurrence and is small because it has no
Database Design
4-39
fields), key_word records and intersect records. Data file tims.d02 contains the
occurrences of record types author, borrower, info, and text. This organization is arbitrary in this case since the database is not large.
Key field id_code is much smaller than keys friend and word and is therefore stored in a
separate key file, as is shown in the example below.
/*-------------------------------------------------------------------Technical Information Management System (TIMS) Database
--------------------------------------------------------------------*/
database tims
{
data file "tims.d01" contains system, key_word, intersect;
data file "tims.d02" contains author, borrower, info, text;
key file "tims.k01" contains id_code;
key file "tims.k02" contains friend, word;
record author {
char name[32];
}
record info {
unique key char id_code[16];
char info_title[80];
char publisher[32];
char pub_date[12];
int info_type;
}
record borrower {
key char friend[32];
long date_borrowed;
long date_returned;
}
record text {
char line[80];
}
record key_word {
unique key char word[32];
}
record intersect {
int int_type;
}
/* name of borrower */
/* dates are stored initially */
/* numeric YYMMDD */
/* line of abstract text */
/* subject key words */
/* copy of info_type */
(continued)
4-40
(continued)
set author_list {
order ascending;
owner system;
member author by name;
}
set has_published {
order ascending;
owner author;
member info by info_title;
}
set articles {
order last;
owner info;
member info;
}
set loaned_books {
order last;
owner info;
member borrower;
}
set abstract {
order last;
owner info;
member text;
}
set key_to_info {
order last;
owner key_word;
member intersect;
}
set info_to_key {
order last;
owner info;
member intersect;
}
set loan_history {
order last;
owner system;
member borrower;
}
}
Database Design
4-41
Chapter 5
Database Manipulation
5.1
Introduction
currency control
data retrieval
data creation
Note that all RDM function names are prefixed by d_ or dt_ (for example, d_keyfind or
dt_opentask) so as to avoid name conflicts with other user or system library functions.
The return value of all RDM functions is an integer completion status for the requested
operation. A status code of zero (S_OKAY) indicates that the operation completed
successfully. These status codes will be introduced as required in the discussions that
follow. A complete list can be found in section 5.8, "Database Error Reporting," and
complete descriptions are provided in the RDM Reference Manual.
Database Manipulation
5-1
The purpose of this chapter is to introduce the use of the principal functions through
explanation and examples. Complete details relating to the use of each function are
provided in the RDM Reference Manual.
Most of the RDM functions must be passed a database number. In the examples, a
constant called CURR_DB has been passed to the functions. The database number must
always be passed, even when only one database is open. See section 5.9.2, "Accessing
Multiple Databases," for complete details regarding multiple database access and the
database number argument.
5.2
Database Control
Database control functions provide control over the runtime systems operational
environment. They provide for the opening and closing of databases, location and names
of database files, initialization of databases and files, and various runtime tuning
parameters and options.
The database control functions are listed in Table 5-1 and have been grouped into two
categories. The "pre-open" functions are those that can only be called prior to a d_open
call, which opens the database. The "post-open" functions can only be called after the
database has been opened. Functions d_off_opt and d_on_opt, however, can be
executed either before or after the database is opened.
5-2
d_dbfpath(dbf_dir)
d_dblog(log_name)
d_dbtaf(taf_name)
d_dbtmp(tmp_dir)
d_ctbpath(ctb_dir)
d_lockcomm(lm_type)
d_lockmgr(lm_name)
d_checkid(userid)
d_dbuserid(userid)
d_open(dbnames, mode)
d_renfile(dbname, FILE,filenm)
d_renclean()
commands.
d_setfiles(num)
d_setpages(num, num)
d_rdmini(ini_dir)
Close database.
d_closeall()
databases).
d_destroy(dbn)
d_iclose(dbn)
d_initfile(FILE, dbn)
d_initialize(dbn)
d_iopen(dbnames)
d_off_opt(options)
d_on_opt(options)
Database Manipulation
5-3
Database access or update functions called prior to a successful opening of the database
will return error code S_DBOPEN.
When d_open is called, the RDM runtime library will allocate and initialize memory
space for all of its internal tables, and will read into memory the database dictionary for
the requested databases. Memory is also allocated for the RDM cache. If there is not
enough memory available for the tables or cache, d_open will return status code
S_NOMEMORY.
Function d_close will close all open databases by flushing all buffers, closing all database
files, and freeing all of its dynamically allocated memory.
Note: The database should always be closed before a program terminates. Failure to do
so could impair the integrity of (that is, corrupt) the database.
If a subsequent d_open call is made before the first database is closed, RDM will abort
any active transaction, close the open database(s), and open the new database(s). (See
section 5.9, "Multiple Database Access," to learn how to open and access more than one
database at a time.)
Database Dictionary
Control files:
vista.taf
vista.ctb
userid.log
5-4
Unless otherwise specified, all files are located in the users current (or working)
directory.
Several techniques are available for providing the path names of alternative directories
for the dictionary, control, and database files.
This example will prefix \timsdb\ to all file specifications as specified in the DDL,
including any directory path. Thus the file specification for the dictionary would be
\timsdb\tims.dbd and the spec for the data file would be \timsdb\tims.dat.
Multiple databases (see section 5.9, "Multiple Database Access") that are located in
different directories can be opened simply by providing d_open with the appropriate
path names, as in the following example:
d_open("\\centura\\tims;\\acme\\tims", "x");
Here, database 0 would be the tims database for Centura and database 1 would be the
tims database for Acme. Both directories would need to contain the dictionary file
tims.dbd even if they are identical. (Under UNIX they could be linked.)
Environment Variables
Five UNIX environment variables can be used to identify the directories that contain the
dictionary, control, and database files.
The directory containing the dictionary files can be specified by an environment variable
named DBDPATH. The directory that is to contain the database files can be specified by
an environment variable named DBFPATH, shown in the following example.
set DBDPATH=\dbdefs\
set DBFPATH=\timsdb\
Note: Function d_open will append a backslash (\), or slash (/) in UNIX, to these path
names if not specified.
Assume that the DDL for database tims includes the following data and key file
statements:
Database Manipulation
5-5
When the database is opened, RDM would expect to find file tims.dbd in directory
\dbdefs (that is, the file specification for the dictionary would be \dbdefs\tims.dbd).
The file specifications for the database files would be \timsdb\centura\tims.dat and
\timsdb\centura\tims.key.
In addition to the above, the d_open call may contain a partial path name:
d_open( "wayne\\tims", "x" );
In this case the additional path specification (wayne\) placement would precede the
DBDPATH or DBFPATH values. The resulting file names are shown below:
\dbdefs\wayne\tims.dbd
\timsdb\wayne\centura\tims.dat
\timsdb\wayne\centura\tims.key
You can provide any combination of path specifications in the environment variables
DBDPATH and DBFPATH, in the d_open statement, and in the schema. The resulting
paths to the data and key files are built according to the following rules:
A path is considered absolute if it starts with a directory character ("/" for UNIX).
Otherwise the path is considered relative. Device (or logical device) specifications
are transparent.
For example, on Windows, c:\users\mis is considered an absolute path, whereas
c:users\mis is relative.
The final path is constructed by concatenating the various paths from the various
sources. Construction stops when the final path becomes absolute.
Construction of the final path works backward. When a path precedes the final path,
any previous device designator is thrown away (for example, a:sys\users put in
front of c:mis results in a:sys\users\mis).
In constructing the final path for the dictionary file, the (optional) path supplied to
d_open is used, preceded by the (optional) path in the environment variable
DBDPATH.
In constructing the final path for the data and key files, the (optional) path in the
schema is used, preceded by the (optional) path supplied to d_open, with the
(optional) path in the environment variable DBFPATH preceding all.
5-6
Database Manipulation
5-7
This open will find the sales dictionary in d:\sales\sales.dbd, the sales data and key files
in c:\dbfiles\, the inventory dictionary in d:\invntory, and the inventory data and key
files in c:\dbfiles\.
The same paths would be used if the second database were incrementally opened:
d_open( "sales", "s" );
...
d_iopen( "invntory" );
If the number of elements in one of the path variables is less than the database number of
an open database, then the "extra" databases get no path. However, if only one path is
given, it applies to all databases. Note that the DBFPATH shown above did not need to
contain the same directory twice. The following value would have the same effect:
set DBFPATH=c:\dbfiles
5-8
A path may contain null elements. If the path ends in a semi-colon, then all subsequent
elements are null and the search will be made in the current directory. For example, the
following DBDPATH contains a null element 1, and DBFPATH contains null elements 1,
2, ...
set DBDPATH=c:\sales;;c:\sales
set DBFPATH=c:\dbfiles;
The location and name of the transaction activity file (see section 6.2, "Operational
Environment") can be specified with environment variable DBTAF. Environment
variable DBLOG can be used to specify the location and name of the database log file. If
either variable ends with the directory separator character ("/" on UNIX), the default file
names are appended to the specified path name in order to form the fully qualified file
name. The default file names are vista.taf for the transaction activity file and userid.log
for the log file, and are assumed to be located in the working (or current) directory. If the
environment variable does not end with the directory separator character, RDM will
assume that the variable is the complete file name.
The userid must be defined either through an environment variable called DBUSERID or
through function d_dbuserid (as described in the next section). There is no default. The
user identifier is used to activate communication between the RDM user and the lock
manager, and is used in the forming of the default name of the users database log file.
Note that the userid is only needed when the database is opened in shared or exclusive
access mode. It is not needed in one-user mode or in the single-user version.
There is only one transaction activity file across the system for each database family, and
it must be accessible to every user. Each user must have his or her own individual log
file, which must also be accessible to all users on the system. Also, all log files for each
database family must be located in the same physical directory.
Database Manipulation
5-9
Function
Name
d_dbuserid
DBDPATH
d_dbdpath
DBFPATH
d_dbfpath
DBLOG
d_dblog
DBTAF
d_dbtaf
LOCKMGR
d_lockmgr
Description
Set database user identifier.
TIMSDAT 0
TIMSKEY 1
5-10
The calls to d_renfile must be issued prior to the call to d_open of database tims. The
name of the database needs to be specified in the d_renfile call, to distinguish between
files contained in different databases if more than one is opened.
Note: When d_open is locating the database files, the database file specification
provided in the d_renfile call overrides the environment variable (DBFPATH) and any
path prefix to the database name. The dictionary and control file specifications are
unaffected.
The d_renfile function has a companion function, d_renclean. Function d_renclean can
only be called while all databases are closed, and will clean up all memory and references
used by d_renfile. This allows switching back and forth between databases as seen in
the following example. In this example, there are two copies of the data files, one copy
that is shared by everyone on the network (drive s:) and the other one on the users hard
drive (drive c:).
d_open("tims", "s");
/* normal processing on network copy */
d_close();
d_renfile("tims", TIMSDAT, "c:\\tims\\tims.dat");
d_renfile("tims", TIMSKEY, "c:\\tims\\tims.key"):
d_open("tims", "o"):
/* processing on the local copy */
d_close();
d_renclean();
*/
d_open("tims", "s");
/* back to processing on the network copy */
d_close();
Database Manipulation
5-11
Note: If d_renfile has been called, d_destroy will remove the new file names and leave
the original ones untouched.
Individual files that have a fileid specified in the DDL file statement can be initialized
using function d_initfile. For example, the following DDL defines files that keep track of
a daily user login history:
key file day_key = "dayfile.key" contains login_id;
data file day_data = "dayfile.dat" contains login_history;
These files are initialized by the application when the user logs in at the beginning of the
day, as follows:
#include "vista.h"
#include "login.h"
#include "mis.h"
...
/* Open Login and Mgt Info System databases */
d_open("login;mis", "s");
/* Since each user has his own login file, an
exclusive lock on that file will preclude the use of
other locks and yield better performance. */
d_reclock(LOGIN_HISTORY, "x", CURR_DB);
/* Initialize daily login files */
d_initfile(DAY_KEY, CURR_DB);
d_initfile(DAY_DATA, CURR_DB);
...
5-12
less than or equal to the operating system limit, minus the maximum number of
additional files the application will have open at a time. The default is a maximum of
eight open files at a time.
For example, normally a standard C program will have (at least) five files open upon
initiation: stdin, stdout, stderr, stdprn, and stdaux . If the operating system limit is 20,
then the following call will ensure that RDM will not open too many files:
d_setfiles(15);
d_open("tims");
Setting the number of open files too small may result in some performance degradation
due to unnecessary file closings and openings.
If there are occasions when the application needs more file handles than are available, it
is possible to tell the runtime to close all files it currently holds open. The d_closeall
function, which takes no parameters, will close all files under RDM control that are
currently open. The files will be re-opened as needed. If this function is used frequently,
overall performance will decrease due to the extra opening and closing of files.
The initial size of the pages allocated for the database cache will be equal to the size of
the largest page in the database. Thus, if the largest page is 4096 bytes long, then the
database cache in the above example will occupy 524K bytes (128 * 4096). The size of the
pages in the overflow cache is fixed at 1024 bytes. If there is not enough memory
available to accommodate the requested number of pages, function d_open will return
status S_NOMEMORY.
Database Manipulation
5-13
Option Settings
Various runtime option settings allow you to do these activities plus others:
Turn on or off the use of delete chain slots (for when new records are created)
Rather than supplying separate functions to control the setting of these runtime options,
RDM provides two parameter-based system option setting functions called d_on_opt
and d_off_opt. These functions are passed a bit status word, which has a bit associated
with each option. These options have been assigned constants in vista.h. See the RDM
Reference Manual for a complete list.
Here are some examples. To turn on delete chain usage and archive logging:
d_on_opt(DBCHAINUSE | ARCLOGGING);
To turn off transaction logging and archive logging:
d_off_opt(TRLOGGING | ARCLOGGING);
Transaction and archive logging are discussed in Chapter 6, "Transaction Processing."
The ability to turn on or off the use of deleted record slots provides some application
control over the placement of related records in the database. If all member record
occurrences of a given set are entered together, and use of the delete chain is turned off,
the records will all be physically placed in the order in which theyre entered at the end
of the data file. This will improve performance when, later, they are all accessed
together.
To establish case-insensitive sorting with keys:
d_on_opt(IGNORECASE );
This option will re-define the collating sequence of characters, and must be used for the
lifetime of a database. You cannot build a database with this option turned on, then later
use it with it turned off.
When an environment requires minimum use of file handles, you may need to use the
CLOSEFILES option, which will cause all open file handles to be closed before an RDM
5-14
function returns. This is often needed in Microsoft Windows. Be sure, however, that this
is needed, because it can have a detrimental effect on performance.
When RDM allocates memory in the Microsoft Windows environment, it will use
LocalAlloc calls when the allocation is small (less than 128 bytes). If local memory is
exhausted, or if the allocation is greater than 128 bytes, GlobalAlloc is used. If an
applications data segment is full, RDM may be instructed to use GlobalAlloc exclusively,
by setting the GLOBALALLOCS option.
The initial settings have transaction logging and delete chain use turned on and archive
logging, case-insensitive sorting, automatic file closing, global allocation, and session
status call restriction turned off.
5.3
Currency Tables
All of the data contained in an RDM database is accessed through use of the currency
tables. Thus, a thorough understanding of the use of these tables is necessary.
The retrieval of RDM data is always a two-step process. First the location of the data is
established, and then the data is read. The located data is always in the form of a specific
record occurrence. The database address of a record is the location in the database where
the record is stored. The currency tables contain database addresses as follows:
current record
current owner
current member The database address of a member record occurrence for the set.
There is a current member entry for each set in the database.
A currency table value is established through the record location functions (for example,
d_keyfind or d_findnm), and through additional functions that directly modify currency
table entries (for example, d_setor copies the current record value to the current owner
entry of the specified set). Once a record has been located, its database address is
automatically stored in the currency table. Its contents can then be read (for example,
d_recread reads the contents of the current record).
Locating record occurrences through set relationships directly involves manipulation of
the currency tables. This process is called set navigation. The currency tables are used
to keep track of the path through the network of set relationships that was followed to
arrive at a particular record occurrence.
Database Manipulation
5-15
A portion of the tims database schema is given in Figure 5-1, with an example of a
possible currency table state showing pointers to particular record occurrences in the
database.
SCHEMA
system
DATABASE
CURRENCY TABLES
system
current owner
author_list
author
Flavin, M.
Knuth, D.
Kruglinski, D.
has_published
abstract
Fundamentals of Info
current member
author_list
info
Fundamental Algorithms
Semi-numerical Algorithms
Searching and Sorting
has_published
abstract
current record
text
5-16
d_setrm(SET, dbn)
d_setor(SET, dbn)
d_setmr(SET, dbn)
d_setmm(SET1, SET2, dbn) Set current member of SET1 from current member of SET2.
Currency Access Functions
d_crget(dba, dbn)
d_crset(dba, dbn)
Database Manipulation
5-17
5.4
Data Retrieval
Record occurrences are located using RDMs key retrieval functions, set navigation
functions, direct access, or any combination of these.
5-18
Code Validation
The following example from the "vehicle make" example illustrates how to use keys to
validate coded data fields.
char vma_desc[25];
struct fleet f;
get_user_info(&f);
The vehicle make code entered by the user as part of the fleet record is used as the key
value argument of the d_keyfind function, to check that a vehicle record for that make
exists in the database. If the record does exist, its description is read (function d_crread)
and is displayed to the user once the record entry is completed. VMA_CODE and
VMA_DESC are constants defined in the database header file created by ddlp. The
second argument to both d_keyfind and d_crread are pointers to the variables that
contain the necessary data.
Database Manipulation
5-19
The d_keyfind call will position to the first check date key equal to start date. If there are
no checks dated 05/01/93 on file, then function d_keynext is called to position to the first
check whose date is greater than the start date. The while loop first reads the value of
the positioned key using function d_keyread. Note that this reads the key value only
and does not read the record. The record contents are only read if the check date
returned from the d_keyread call is within the desired range. Function d_recread reads
the contents of the current record for display by the subsequent printf calls. The final
d_keynext call positions to the next check date key.
Complex Searches
The m.o. (modus operandi) example of Chapter 4, "Database Design," introduced how
complex searches can be rapidly performed through the use of keys. The m.o. record
consists of a single 25-byte key field in which each element of the array represents a
coded m.o. attribute.
The simplest approach is to scan through all of the mo_data keys, checking each one for a
match, as follows:
5-20
char mo_key[25];
/* m.o. key is 25 byte array */
char mo_search[25]; /* user entered search data */
int status;
...
for (
status = d_keyfrst(MO_DATA, CURR_DB);
status == S_OKAY;
status = d_keynext(MO_DATA, CURR_DB) )
{
d_keyread(mo_key);
if (mo_match(mo_key, mo_search))
...
/* report match */
}
Function mo_match checks the m.o. key against the m.o. search data entered by the user,
returning true (that is, non-zero) if they match and false (that is, zero) if they do not. A
zero value in an m.o. search data element means that attribute is not to be used in the
search.
/* Check for matching m.o.s */
mo_match(mo1, mo2)
char *mo1;
char *mo2;
{
int x;
for ( x = 0; x < 25; ++x )
{
if ((mo2[x] != 0) && (mo1[x] != mo2[x]))
return (0);
}
return (1);
}
Since keys are sorted, the number of keys that need to be scanned can be reduced when
the first element (and any subsequent elements) is non-zero. For example, if the user has
supplied m.o. values for the first three elements, a key scan of only the keys on file with
those values can be performed, yielding great performance improvements.
Database Manipulation
5-21
char mo_key[25];
/* m.o. key is 25 byte array */
char mo_search[25]; /* user entered search data */
int mo_prefix;
/* initial non-0 elements in mo_search */
int lc;
/* loop control index */
...
/* compute mo_prefix */
for (mo_prefix = 0; mo_search[mo_prefix] != 0; ++mo_prefix)
;
/* count number of initial non-0 elements */
/* initialize mo_key */
for (lc = 0; lc < 25; ++lc)
{
if ( lc < mo_prefix )
mo_key[lc] = mo_search[lc];
else
mo_key[lc] = 0;
}
/* position to first key with matching prefix */
if (d_keyfind(MO_DATA, mo_key, CURR_DB) == S_NOTFOUND)
d_keynext(MO_DATA, CURR_DB);
/* scan all keys with matching prefix */
while (db_status == S_OKAY)
{
d_keyread(mo_key);
/* ensure prefix still matches */
for (lc = 0; (lc < mo_prefix) && (mo_key[lc] == mo_search[lc]); ++lc)
;
if (i < mo_prefix)
break;
/* prefix doesnt match - scan ends here */
if (mo_match(mo_key, mo_search))
...
/* report match */
d_keynext(MO_DATA, CURR_DB);
}
Notice that this example is very similar to the example of retrieval by a range of values.
Also notice that if the first element of mo_search is zero, all mo_data keys will be
checked.
5-22
record borrower
{
key char
fr_last[16];
char
fr_first[16];
long
date_borrowed;
long
date_returned;
compound unique key friend
{
fr_last;
fr_first;
}
}
Note that the last name may be used as a key by itself. A new key definition, named
friend, is defined in the record. (It must also be included in a key file list.)
When ddlp encounters a compound key, it creates a special structure for the key in the
header file it generates. In this case, within tims.h will be the following structure
definition:
struct friend {
char fr_last[16];
char fr_first[16];
};
The definition is intended to be used to perform searches for compound keys, as in the
following code fragment:
#include "tims.h"
...
struct friend fr;
...
printf("Last name: ");
gets(fr.fr_last);
printf("First name: ");
gets(fr.fr_first);
if (d_keyfind(FRIEND, &fr, CURR_DB) == S_NOTFOUND)
printf( "No one by that name found\n" );
else {
/* use the borrower record */
Find the record that is the owner of the set whose members are to be read. Typically,
this can be done using keys, by iteratively applying this procedure, or by a
combination of both.
Database Manipulation
5-23
2.
Make the located owner record the current owner of the set to be traversed, using an
appropriate currency manipulation function (such as d_setor).
3.
Find the members of the set, using the set navigation functions (for example,
d_findnm will set the current record and current member of the set to the next
member record).
Each of the set member find functions (d_findXm) will set both the current member and
the current record to point to the found record occurrence. A reciprocal function,
d_findco, will set the current owner of the specified set from a current record that is
connected through the specified set. The currency changes made by each RDM function
are identified in the function descriptions in the RDM Reference Manual.
As an example of the above procedure, consider the transactions set from the checking
account example in Chapter 4, "Database Design." The budget record is the owner and
the check record is the member. The following code will display all of the checks written
against budget category FOOD.
/* locate the budget record for the FOOD budget */
d_keyfind(CODE, "FOOD", CURR_DB);
/* make the FOOD budget the current owner of set transactions */
d_setor(TRANSACTIONS, CURR_DB);
/* find each member of the set and read and print its contents */
for (
d_findfm(TRANSACTIONS, CURR_DB);
db_status == S_OKAY;
d_findnm(TRANSACTIONS, CURR_DB))
{
d_recread(&chk, CURR_DB);
... /* print check record */
}
The set navigation procedure above describes a top-down navigation, wherein the owner
is located and then the members. RDM also provides the ability to first locate a member
and then the owner by using function d_findco, which finds the owner of the current
record for the specified set. For example, the following code will locate a check record by
check number, and then find and print its budget category.
5-24
Many-to-Many Navigation
The navigation of the students to classes, many-to-many, set example in Chapter 4,
"Database Design," is shown in the following code, which lists the students registered in
class CS101.
struct class crec; /* class record variable */
struct student srec;/* student record variable */
/* find CS101 class record */
d_keyfind(CLASS_ID, "CS101", CURR_DB);
/* read contents of class record */
d_recread(&crec, CURR_DB);
/* make the class record owner of the my_students set */
d_setor(MY_STUDENTS, CURR_DB);
/* scan each member of my_students set */
while (d_findnm(MY_STUDENTS, CURR_DB) == S_OKAY)
{
/* find student record which owns current intersect record */
d_findco(MY_CLASSES, CURR_DB);
/* read and print contents of student record */
d_recread(&srec, CURR_DB);
printf("CS101: %s\n", srec.name);
}
Note that function d_setor actually sets both the current owner and the current member
of set my_students. The current owner is set to the CS101 record, and the current
Database Manipulation
5-25
member is set to NULL_DBA. This allows the initial call to d_findnm to return the first
member of the set. The output from execution of the above code might be:
CS101:
CS101:
CS101:
CS101:
Carlson
Jones
Kelly
Smith
From this example you should be able to produce the code for listing the classes in which
a particular student is registered.
Each member of the cust_notes set is an occurrence of either the text30, text55, or text80
record types. Here it does not matter which record type is read, since each contains only
one string field. The character array text is used to store the contents of all. The function
d_crtype, however, could have been used to determine the type of the text records.
5-26
Figure 5-2 illustrates the operation of function by_key up to the point where function
pr_keywords is called. The d_keyfind locates the key_word record occurrence, which is
then made the current owner of set key_to_info. As each intersect record that is a
member of the info_to_key set is found (using d_findnm(KEY_TO_INFO, CURR_DB)),
its owner through the info_to_key set is found (using d_findco(INFO_TO_KEY,
CURR_DB)), and the info record contents are read. The author is found (and made
current) through the has_published set (using d_findco(HAS_PUBLISHED, CURR_DB))
and the name is read using function d_crread to read a field of the current record.
Database Manipulation
5-27
author
d_keyfind(WORD, key)
d_findco(HAS_PUBLISHED)
key_word
info
d_setor(KEY_TO_INFO)
d_findco(INFO_TO_KEY)
d_findnm(KEY_TO_INFO)
intersect
5-28
The abstract is printed by function pr_abstract, which simply scans through the abstract
set owned by the current info record to read and display each line of abstract text.
Database Manipulation
5-29
/* Print abstract */
pr_abstract()
{
long count;
/* number of abstract members */
char txt[80];
/* line of abstract text */
/*
The other retrieval requirement is to be able to list all of the publications in the tims
database that are by a particular author. This is accomplished with function by_author.
The author is located by scanning through the (system-owned) author_list set, and
comparing a user-specified name with the author name. When a match is found, each
info record owned by the located author is read and displayed, along with its associated
key words and abstract.
5-30
Database Manipulation
5-31
d_reclast
d_recnext
d_recprev
When the retrieval order is not important (the physical order of records on a file may
seem random), the sequential functions are the quickest way to scan records. The id_list
function is an example of such a scan.
/* produce a quick sequential listing of all id_codes */
id_list()
{
char id_code[17];
for (
d_recfrst(INFO, CURR_DB);
db_status == S_OKAY;
d_recnext(CURR_DB))
{
d_crread( ID_CODE, id_code, CURR_DB);
printf("%s\n", id_code);
}
}
These functions maintain their own currency by record type. In a loop, these four
functions will maintain their own positional information, even if there are other functions
in the loop that change currency table settings. To establish or restore a position from
which to continue scanning, the function d_recset can be used. This function will set the
current position of a sequential scan from the current record. Thus if a
d_recfrst/d_recnext loop needs to contain an inner loop for a different record type, the
position can be saved and restored around the inner loop, as follows:
DB_ADDR t1dba;
int status1, status2;
for (
status1 = d_recfrst(TYPE1, CURR_DB);
status1 == S_OKAY;
status1 = d_recnext(CURR_DB))
{
d_crget(&t1dba, CURR_DB);
...
for ( status2 = d_reclast(TYPE2, CURR_DB);
status2 == S_OKAY;
status2 = d_recprev(, CURR_DB))
{
...
}
d_crset(&t1dba, CURR_DB);
d_recset(TYPE1, CURR_DB);
}
5-32
The function page_full begins with the current record (which is assumed to be a
key_word type), and creates a list of the next twenty key word occurrences, as shown
below:
/* create a list of key words for display */
page_full( pglist, dir )
char *pglist[20];
/* list of key words */
int dir;
/* direction of the scan */
{
int i, status;
for (
{
d_crread(WORD, pglist[i], CURR_DB);
}
}
Functions d_recread, d_crread, d_csoread, and d_csmread read part or all of the contents
of the current record, owner, or member, copying the data to an application program
defined buffer. A pointer to this buffer is an argument to each of these functions.
Functions d_decode_dba and d_encode_dba are used to decode and encode a database
address with its file number and slot number. It can be used in conjunction with
functions d_crget and d_crset to utilize a records database address as a primary key,
which can be displayed and referenced by a user in order to directly access individual
record occurrences. For example, a database address for the record at slot number 17112
on file number 11 could be displayed as a primary key field called id number that, to the
user, could look like the following:
id number:
11-17112
By always displaying this number (actually the database address) when a record is
displayed, and by requiring this field to be entered whenever the record is modified, the
record can be located in a single disk read. The code to display the field follows.
DB_ADDR dba;
/* database address of current record */
short file;
/* file number */
unsigned long slot; /* slot number */
...
/* record to be displayed is current record */
d_crget(&dba, CURR_DB);
d_decode_dba(dba, &file, &slot);
printf("id number: %d-%ld\n", file, slot);
Database Manipulation
5-33
When the user has entered an id number, the record can be read as follows:
DB_ADDR dba;
/* database address */
int file;
/* file number */
unsigned long slot; /* slot number */
struct record rec; /* RDM record buffer */
...
/* extract file number of slot number from id number string */
... (you supply the tedious stuff)
/* form database address */
d_encode_dba(file, slot, &dba);
/* set current record and read record contents */
d_crset(&dba, CURR_DB);
d_recread(&rec, CURR_DB);
/* display record */
...
5.5
Data Creation
The RDM functions used to create record and key occurrences and to make set
connections are listed in Table 5-5 below.
Table 5-5. Data Creation Functions
Record/key Creation Functions
d_fillnew(REC, val, dbn)
d_setkey(FLD, val, dbn)
d_makenew(REC, dbn)
d_crwrite(FLD, val, dbn)
d_keystore(FLD, dbn)
New record occurrences are entered into the database by using either the d_fillnew or
d_makenew function. To d_fillnew is passed the record type of the record to be created
and a pointer to the records value. This pointer usually points to a variable of that
records struct type, as declared in file dbname.h. Function d_makenew creates an
(almost) empty record occurrence, in which the field values will be stored later, usually
through individual calls to d_crwrite. However, d_makenew must store the key fields in
the record and create the key file entries at the time of the call. Prior to the call to
d_makenew, function d_setkey must be called for each (non-optional) key field, to save
the values of each key field for d_makenew. Generally, it is simplest to use only
5-34
d_fillnew, which automatically creates the key entries for you without a call to d_setkey.
Function d_makenew is useful when a record is to be created before the contents of the
fields are known, or for creation of records that have no fields (such as an empty
intersection record of a many-to-many set).
Function d_keystore is used to create the key file entries for optional keys in the current
record. Optional keys are often used to defer key creation until non-peak system load
times, in order to maximize data entry performance.
The data creation process involves not only record creation, but set creation as well.
After a record has been created, it often needs to be connected to appropriate sets. This
may involve data retrieval to set up the currency tables properly.
Entry of an info record into the tims database provides a good illustration of what is
typically involved in creating RDM data.
Three functions are presented below. Function ent_info enters the info record and, if
necessary, the author record, and makes the proper set connections. Function
enter_key_words is called by enter_info to read each key word from the user and set up
the many-to-many relationship with the entered info record. Function enter_abstract is
called to read from the user each line of the abstract and connect it to the info record
through set abstract. One function that is called but not shown is function get_info,
which reads from the user, the author name, and info record fields.
Database Manipulation
5-35
#include <stdio.h>
#include "vista.h"
#include "tims.h"
static struct info irec;
/* info record variable */
static struct author arec; /* author record variable */
/* Enter technical information records into TIMS database */
ent_info()
{
char s[32];
/* generic string variable */
int status;
/* enter tech info into TIMS database */
while (get_info() != EOF)
{
/* see if author exists */
for ( status = d_findfm(AUTHOR_LIST, CURR_DB);
status == S_OKAY;
status = d_findnm(AUTHOR_LIST, CURR_DB))
{
d_crread(NAME, s, CURR_DB);
if (strcmp(arec.name, s) == 0)
break;
/* author record on file */
}
if (status == S_EOS)
{
/* author not on file
-- create record and connect to author list */
d_fillnew(AUTHOR, &arec, CURR_DB);
d_connect(AUTHOR_LIST, CURR_DB);
}
/* make author current owner of has_published set */
d_setor(HAS_PUBLISHED, CURR_DB);
/* create new tech. info record */
if (d_fillnew(INFO, &irec, CURR_DB) == S_DUPLICATE)
printf("duplicate id_code: %s\n", irec.id_code);
else
{
/* connect to author record */
d_connect(HAS_PUBLISHED, CURR_DB);
/* set current owner for key words and abstract */
d_setor(INFO_TO_KEY, CURR_DB);
d_setor(ABSTRACT, CURR_DB);
enter_key_words();
enter_abstract();
}
}
}
After the data has been collected from the user (get_info), function ent_info is used to
scan the author_list set for the user-specified author name. If no match is found
5-36
(status == S_EOS), an author record is created and connected to author_list (the current
owner of author_list is always the system record). The found or newly created author
record is set as the current owner of set has_published, using d_setor. The info record is
created and if its id_code is not a duplicate, the id is connected to its author record. The
new info record occurrence is then made the current owner of sets info_to_key and
abstract. Key words and abstract text will be connected to it by the function calls that
follow.
/* Enter any key words */
static enter_key_words()
{
char s[32];
for ( ; ; )
{
printf("key word: ");
if ((gets(s) == NULL) || (s[0] == \0))
break;
/* see if key word record exists */
if (d_keyfind(WORD, s, CURR_DB) == S_NOTFOUND)
{
/* create new key word record */
d_setkey(WORD, s, CURR_DB);
d_makenew(KEY_WORD, CURR_DB);
}
/* create empty intersection record */
d_setor(KEY_TO_INFO, CURR_DB);
d_makenew(INTERSECT, CURR_DB);
d_crwrite(INT_TYPE, &irec.info_type, CURR_DB);
d_connect(KEY_TO_INFO, CURR_DB);
d_connect(INFO_TO_KEY, CURR_DB);
}
}
Database Manipulation
5-37
Function enter_abstract is very simple. As each line of abstract text is entered, the text
record is created and connected to the info record, which is the current owner of set
abstract.
5.6
Data Modification
The functions used to modify fields, records, and sets are shown in Table 5-6.
Table 5-6. Modification Functions
Record/Key Modification Functions
d_recwrite(val, dbn)
d_crwrite(FLD, val, dbn)
d_csmwrite(SET, FLD, val, dbn)
d_csowrite(SET, FLD, val, dbn)
5-38
Optional keys are automatically modified if a key existed for the old value when its field
was modified. If a key entry did not exist for the old value, none will be created for the
new value. However, the fields value in the record is updated.
5.7
Data Deletion
A record can only be deleted if it is not connected as an owner or member of any sets.
The general deletion procedure is, therefore, to disconnect a record from all sets for
which it is a member and disconnect all members from sets owned by that record, and
then delete the record. All disconnections and the delete can be performed with one call
by using function d_disdel.
Function d_keydel is used to delete the key entry of an optional key field in the current
record. The field value in the record itself, however, does not change. All key entries,
including optional keys, are deleted from their respective key files when a record is
deleted using either function d_delete or d_disdel.
Below is the code for function del_info, which deletes an info record from the tims
database.
Database Manipulation
5-39
#include <stdio.h>
#include "vista.h"
#include "tims.h"
/* Delete technical information records from tims database */
del_info()
{
struct info irec;
long count;
char id[16], name[32];
/* find info to be deleted */
printf("id_code: " );
gets(id);
if (d_keyfind(ID_CODE, id, CURR_DB) == S_NOTFOUND)
{
printf("id_code %s not on file\n", id);
return;
}
d_recread(&irec, CURR_DB);
/* get author name */
d_findco(HAS_PUBLISHED, CURR_DB);
d_crread(NAME, name, CURR_DB);
... /* confirm delete request */
... /* disconnect and delete any listed articles */
... /* disconnect and delete borrowers */
/* disconnect and delete abstract */
d_setom(ABSTRACT, HAS_PUBLISHED, CURR_DB);
while (d_findfm(ABSTRACT, CURR_DB) == S_OKAY)
{
d_discon(ABSTRACT, CURR_DB);
d_delete(CURR_DB);
}
/* disconnect and delete intersect and (possibly) key word */
d_setom(INFO_TO_KEY, HAS_PUBLISHED, CURR_DB);
while (d_findfm(INFO_TO_KEY, CURR_DB) == S_OKAY)
{
d_discon(INFO_TO_KEY, CURR_DB);
d_setmr(KEY_TO_INFO, CURR_DB);
d_discon(KEY_TO_INFO, CURR_DB);
d_delete(CURR_DB);
d_members(KEY_TO_INFO, &count, CURR_DB);
if (count == 0L)
{
/* delete key word */
d_setro(KEY_TO_INFO, CURR_DB);
d_delete(CURR_DB);
}
}
(Continued)
5-40
(Continued)
/* disconnect info record from author and delete */
d_discon(HAS_PUBLISHED, CURR_DB);
d_delete(CURR_DB);
/* delete author too, if he has no other pubs */
d_members(HAS_PUBLISHED, &count, CURR_DB);
if (count == 0L)
{
d_setmo(AUTHOR_LIST, HAS_PUBLISHED, CURR_DB);
d_discon(AUTHOR_LIST, CURR_DB);
d_delete(CURR_DB);
}
}
Function del_info first prompts the user for the id code of the info record to be deleted.
If the id code is on file, the info record contents are read into variable irec, the author
name is found from the owner of info through set has_published, and a confirmation of
the delete is requested by displaying the data (not shown).
If the info item to be deleted is a journal or magazine, any articles contained in it must
also be deleted. This is done first (not shown).
Next, any borrower records that are members of that info item are disconnected and
deleted (also not shown).
The abstract is deleted by repeatedly disconnecting and deleting the first member of the
abstract set until there are no more members. Note that the current owner of abstract
had to be initially set to the info record that is the current member of has_published (as
established by the earlier d_findco(HAS_PUBLISHED) call).
Deletion of the key words associated with the info record is similar, but more
complicated. Intersect records are deleted just like the abstract was, except that they
need to be disconnected from set key_to_info as well as from set info_to_key. If the
key_to_info set is empty after deleting the intersect record, the key_word must also be
deleted. Note the call to d_setro to set the current record from the current owner of set
key_to_info (that is, the key_word record). This is necessary because function d_delete
deletes the current record, and the key_word record was not the current record.
The info record is disconnected from its final set, has_published, and can now be
deleted. Function d_discon disconnects the current member from the specified set and
makes the deleted record the current record.
One last task is a check to see if the author has other info items in the database. If not,
then the author record is disconnected from set author_list and is deleted as well.
Database Manipulation
5-41
5.8
All RDM runtime functions return an integer database status code as the value of the
function. These status codes are classified into the following three categories:
User Errors
System Errors
Function Statuses
When user errors are reported, either the arguments to an RDM function are not correct
or the database environment has not been properly set up for the called function. For
example, function d_csoread will return user error S_NOCO if the current owner of the
specified set is NULL_DBA. All user errors in an RDM program should be corrected.
Only one system error is recoverable. The system error S_NOSPACE is returned when
there is no more space available on the disk, but it is recoverable. A recovery technique
is described with function d_trend in the RDM Reference Manual. All other system errors
(and sometimes even error S_NOSPACE) indicate that a serious error has occurred and
processing should be terminated immediately. Usually, these errors result from an
application programming error that corrupts the RDM runtime environment. These
errors are caused by improper use of pointers, by memory mismanagement, and by
operations on strings that are not terminated by a null byte. The most common pointer
error is passing a function argument by value rather than by reference (that is, rather
than by using a pointer).
An external integer variable called db_status always contains the current value of the
database status in the single-tasking, static-link libraries, as assigned by the most recently
called RDM function. It may be referenced by an application program as needed.
The standard RDM C header file, vista.h, contains constant definitions for all status and
error codes that can be returned from an RDM function. This file should be #included in
each C source file that uses RDM. Detailed explanations for each status code can be
found in the RDM Reference Manual.
The internal function dberr is automatically called by the RDM runtime functions
whenever a user or system error occurs. Whenever a user or system error occurs, this
version of dberr will display the error code and description, and then prompt for a
5-42
<Return> to continue. You may need to customize the error reporting to make it more
suitable for the user interface style of your application.
The recommended method is to use d_set_dberr, which allows you to supply the RDM
runtime with a callback function to call when reporting an error. This is the only
customization method available when using Microsoft Windows DLLs. After calling
d_set_dberr, the dberr function will call the function you supply, instead of printing the
message itself. Since dberr is calling your function, you do not need to set db_status or
return a value. Your error reporting function should be declared as follows:
void EXTERNAL_FIXED my_dberr(int, char *);
The first parameter is the error number, and the second parameter is the textual message
that dberr would have printed regarding the error. The void EXTERNAL_FIXED
preceding the function name ensures that its declaration will match that expected by
RDM. The macros are defined in vista.h. Use the function as follows:
#define "vista.h"
void EXTERNAL_FIXED my_dberr(int, char *);
...
main()
{
...
d_set_dberr(my_dberr);
...
}
void EXTERNAL_FIXED my_dberr(err_no, err_msg)
int err_no;
char *err_msg;
{
/* Special handling of some error codes here */
if (err_no <= -900)
{
/* take care in calling d_close, because it may
call dberr again */
exit(err_no);
}
...
/* You may choose to ignore some codes */
if (err_no == S_NOCM)
return;
...
/* Report errors */
...
return;
}
Database Manipulation
5-43
5.9
RDM allows more than one database at a time to be opened and accessed within a single
application program. This capability has been implemented with very little performance
impact for those RDM applications that only need to access a single database. Section
4.4.1, "Logical Design Considerations," described some of the uses of multiple database
access from a database design standpoint. This section explains how to open and access
multiple databases.
Here, the databases named genledg, acctsrec and acctspay are all opened for shared
access. Each is assigned a number by the system in order from left to right, beginning
with zero. Thus database 0 is genledg, database 1 is acctsrec, and database 2 is acctspay.
These numbers are used to specify to the runtime functions which database to access.
After the d_open call, database genledg will be the current database.
5-44
It is not necessary to open all databases at one time. The function d_iopen will
incrementally open a new (set of) database(s) in the same open mode as the already
opened databases. The following statements would be equivalent to the example above,
except that the current database will be two instead of zero:
if (
(d_open("genledg","s") != S_OKAY) ||
(d_iopen("acctsrec") != S_OKAY) ||
(d_iopen("acctspay") != S_OKAY) )
{
if (db_status == S_UNAVAIL)
{
printf("database(s) not currently available\n");
exit(1);
}
}
Whether the databases are opened all at once, or incrementally, they may all be closed
with the d_close call. They may be incrementally closed with the d_iclose call. For
example, after the above opens, the call:
d_iclose(1);
will close the acctsrec database. All database numbers will shift accordingly (in this case,
the database number for acctspay will shift from two to one). If database 2 (acctspay)
was the current database, then database 1 (still acctspay) will be the current database
following the d_iclose.
In addition to the current database, a current record is maintained for each database.
Whenever the current database is changed, the current record for the old database is
saved and the current record for the database that is to be made current is restored.
Database Manipulation
5-45
Method 1
In this method, function d_setdb is called to set the current database. The dbn parameter
must be -1 (or its equivalent CURR_DB). An example of this technique follows.
#include "vista.h"
...
d_open("genledg;acctsrec", "s");
...
/* enter billing record into acctsrec database */
d_setdb(1);
d_fillnew(BILLING, &bill, CURR_DB);
/* find and update ledger account in genledg database */
d_setdb(0);
d_keyfind(ACCT_ID, bill.gl_id, CURR_DB);
d_recread(&glacct, CURR_DB);
..
/* update gen. ledger account record */
d_recwrite(&glacct, CURR_DB);
Method 2
In this method, the database number is passed as to the RDM functions that access or
control database content. An example of this technique follows.
d_open("genledg;acctsrec", "s");
...
/* enter billing record into acctsrec database */
d_fillnew(BILLING, &bill, 1);
/* find and update ledger account in genledg database */
d_keyfind(ACCT_ID, bill.gl_id, 0);
d_recread(&glacct, 0);
..
/* update gen. ledger account record */
d_recwrite(&glacct, 0);
Mixed Method
It is possible to use d_setdb to set the current database, but override it with an explicit
database number when necessary.
d_open("genledg;acctsrec", "s");
...
/* enter billing record into acctsrec database */
d_setdb(0);
d_fillnew(BILLING, &bill, 1);
/* find and update ledger account in genledg database */
d_keyfind(ACCT_ID, bill.gl_id, CURR_DB);
d_recread(&glacct, CURR_DB);
..
/* update gen. ledger account record */
d_recwrite(&glacct, CURR_DB);
5-46
The DBUSERID
The DBDPATH
The DBFPATH
The CTBPATH
The status of all key files (position, last found value, etc.)
The complete data dictionary (or dictionaries, if multiple databases are open)
The transaction id
Context switching within RDM is the ability to save and restore this task-specific data in
an allocated structure. With this capability, multiple contexts can be operated on by the
same copy of the runtime library. Typically, a context is associated with a task, which
RDM defines as an independent unit of code execution.
This is a deliberately broad definition. You can program multiple database tasks within a
single process or you can run Windows with the DLL version of RDM in multiple
windows. In each case, a task would operate within one RDM context.
Database Manipulation
5-47
dt_newcache(DB_TASK *, int)
dt_sharecache(DB_TASK *, DB_TASK *)
dt_closetask(DB_TASK *)
5-48
#define LOCKCOMM_DATA
#include "vista.h"
DB_TASK ControlTask;
DB_TASK ClientTask;
main()
{
struct client cl2;
dt_opentask(&ControlTask);
dt_open("ctrl", "x", &ControlTask);
/* Initialize control database */
...
dt_opentask(&ClientTask);
dt_open("client1;client2;client3", "s", &ClientTask);
...
/* Access client database */
dt_fillnew(CLIENT, &cl2, &ClientTask, 2);
...
dt_close(&ClientTask);
dt_closetask(&ClientTask);
...
dt_close(&ControlTask);
dt_closetask(&ControlTask);
}
Two variables of type DB_TASK are defined. One context is created to open a single
control database in exclusive access mode. Then a second context is created to open a
group of shared databases, each representing one client.
A set of "cover" macros have been provided that allow you to use the familiar d_ function
names in applications that will use just one context. A tasking application that uses the
macros would look like this:
#define LOCKCOMM_DATA
#include "vista.h"
DB_TASK Currtask;
main()
{
dt_opentask(&Currtask);
/* Normal RDM code*/
d_open("db", "x");
d_close();
dt_closetask(&Currtask);
}
Currtask is the DB_TASK variable name that is automatically supplied by the macros.
For example, the d_open call above is translated to:
dt_open("db", "x", &Currtask);
Database Manipulation
5-49
You must compile your application with the -DMULTI_TASK command line argument
or place #define MULTI_TASK in your source file.
There are two functions that do not take a task parameter. They are:
d_decode_dba
d_encode_dba
Note: DB_TASK variables can not be shared between applications through DLLs. Each
application must have its own DB_TASK variables. If the DB_TASK variables are located
in the DLL instead of the application, the DLL must somehow track which application is
using which DB_TASK.
The Microsoft Windows Guide to Programming (Microsoft Press 1990, section 20.2.1) states
the following:
DLLs can be used to share objects between applications. Certain types of objects, including
code and resources, can be freely shared using a DLL. The sharing of other type of objects,
including data and file handles, is much more limited. This is because file handles and data
are created in an applications private address space. Attempts to share file handles, or to
share data (outside of DDE, the clipboard, and the librarys data segment) will lead to
unpredictable results, and could be incompatible with future versions of Windows.
5-50
Note: A database that is built with a particular country table must always use the same
country table, because sorted sets and keys will appear to be corrupted to RDM because
they are out of order if the table is changed or omitted.
When a country table is being used for sorting purposes, single character fields will be
compared using the country table when they are declared as signed (e.g., char
my_field;). Unsigned character fields will be sorted without the use of the country table.
The country table has the following format:
w,x,y,zz
w,x,y,zz
...
where:
w
x
y
z
=
=
=
=
The input character, w, will be displayed as the character x in ida, datdump, and
keydump, since these utilities interpret the data. It will be sorted as though it were z.
The y (or subsort) value indicates what to do when this character sorts to a value equal to
an existing character. The z (or sort-as) field can contain one or two characters. If it
contains two characters, then the input character will be sorted as though it were two
characters. By default any character not listed as an input character within the country
table gets sorted and displayed as itself.
When two or more characters are mapped to the same subsort character(s), they become
members of a grouping. The subsort value controls how characters in a group are
subsorted within the group. A zero means that the two input characters will be treated
as identical. A one means that the input characters are considered part of the same
group, but are sorted by ASCII value within the group.
As an example, look at the country table and data below:
a,a,0,A
b,b,0,B
c,c,0,C
...
z,z,0,Z
Database Manipulation
ab1
AB2
AA3
aa4
bb5
aB6
Ab7
BB8
5-51
The left column below shows how this data would be sorted. The right column shows
the internal representation used during the sort.
AA3
aa4
ab1
AB2
aB6
Ab7
bb5
BB8
AA3
AA4
AB1
AB2
AB6
AB7
BB5
BB8
Note that case in the original data is intermixed after the sorting. Because of the zero
subsort value, if we used d_recwrite to change the bb5 to BB5, RDM would not write the
change to disk since, according to the country table, the data did not change. Note that
this mapping of the lowercase letters to the uppercase letters with a subsort value of zero
is exactly what ignorecase (explained later in this section) does in the country table.
Suppose we use the same data with the same country table but we change all the subsort
values to one as listed below,
a,a,1,A
b,b,1,B
c,c,1,C
...
z,z,1,Z
ab1
AB2
AA3
aa4
bb5
aB6
Ab7
BB8
The sorting sequence will be changed from the normal ASCII ordering below
A B C ... Z ... a b c ... z
to the new ordering of:
A a B b C c ... Z z
5-52
The left column below shows how the above data will be sorted. The right column
shows the internal representation used during the sort.
AA3
AB2
Ab7
aa4
aB6
ab1
BB8
bb5
AA3
AB2
A B7
AA4
AB 6
AB1
BB8
BB5
Since the subsort value of one means that the input characters are not identical, bb5
could be easily changed to BB5 because RDM would see the bb and BB as not exactly the
same.
One other point to note is that the country table processing is a one-pass lookup; it will
not recursively lookup characters. For example,
d,d,0,a
a,a,0,A
maps d to a and also maps a to A. However, transitive properties do not apply, which
means that d would not be mapped to A. To get a and d to be sorted as the same
character(s), the exact sort-as string must be used for both as shown below:
d,d,X,A
a,a,X,A
An additional country table directive may appear as the first line in vista.ctb. The first
line may say ignorecase, which will cause all lowercase letters (a - z) to have their sort-as
characters changed to their uppercase counterparts (A - Z) using a subsort value of zero.
Sorting will ignore the case of the letters, but the display of the characters will not be
changed. If the ignorecase option is not used, it is necessary to specify all of the sort-as
characters in uppercase to get the same effect.
A possible German country table is shown below:
,,0,AE
,,0,ae
,,0,UE
,,0,ue
,,0,OE
,,0,oe
,,0,ss
Database Manipulation
5-53
5-54
The use of the functions eliminates the need for the country table file, but there are big
disadvantages to using the functions instead of the country table file. The country table
file is used by all RDM utilities and applications that call d_open. Hence if a database is
built by an application using the above function calls, and a utility such as ida is used on
the database, it may find the keys in an order other than expected. Worse yet, if it adds
keys to the database, it will do so with a different collating sequence, which will cause
the key files to become corrupted.
The safest way to use international characters is to define them in a country table that
will always be used when the database is used. If the function calls are used in
application code (to eliminate the extra file), it will still be necessary to use a country
table file when the utilities are used on the same database. Alternatively, the ignorecase
and CTBPATH information can be put into the rdm.ini file.
Database Manipulation
5-55
Chapter 6
Transaction Processing
6.1
Introduction
Transaction Processing
6-1
6.2
Operational Environment
6-2
6.3
Three functions are provided in RDM to support transaction processing. These are listed
in Table 6-1. Function d_trbegin is used to mark the beginning of a transaction. All
changes specified after the d_trbegin call are written to the database at the same time,
when function d_trend is called to end the transaction. If any errors are detected, or if a
requested lock is not available after d_trbegin has been called, function d_trabort can be
called to abort the transaction. Function d_trabort will discard any changes that have
been made since the beginning of the transaction, ensuring that the state of the database
is the same after d_trabort as it was before d_trbegin.
Table 6-1. Transaction Processing Functions
d_trbegin(trx_id)
Begin transaction
d_trend()
End transaction
d_trabort()
Abort transaction
When a database is opened in shared access mode (see Chapter 7, "Multi-User Database
Control"), all database updates must be made from within a transaction (unless the files
being updated have been exclusively lockedsee section 7.3.4, "Exclusive Locks"). When
using one-user or exclusive modes, or when using the single-user library, transactions are
not required, but are recommended for all database updates.
Function d_trbegin is called to notify the RDM runtime of the start of a new transaction.
Transactions cannot be nested within a DB_TASK. If d_trbegin is called from within an
active transaction, error code S_TRACTIVE will be returned.
Function d_trend completes a transaction by committing all changes to the database.
Function d_trabort disregards all changes proposed since the d_trbegin call, and frees all
unkept and non-exclusive file locks.
Transaction Processing
6-3
6.4
Transaction Processing
A single transaction performs a great deal of work in order to guarantee the successful
commit of an update. The following list shows the sequence of function calls to be made
by an application, along with the underlying action performed by RDM.
Table 6-2. Actions of Transaction Processing Functions
Function
Action
d_trbegin
d_lock
transaction body Here, a series of function calls to modify the database are made.
If modified pages fill the cache (called a transaction overflow), then
extra cache space is created by writing modified pages to the log file.
d_trend
During this call, the commit is made to the database. At any point
after the d_trbegin and before the d_trend, updates can be thrown
away and locks released by issuing a d_trabort call. During a d_trend,
the following sequence occurs:
1.
2.
3.
4.
5.
6.
7.
One log file is maintained for each user. This file is used within a transaction for storing
modified database pages after the page buffers (cache) have been filled. Thus, there is
effectively no limit to the number of changes that can be made within a transaction.
When the transaction ends, all modified pages are first written to this file (whether or not
an overflow occurred). The database is then updated.
6-4
Note: The size of your transactions should be carefully balanced. Even the most trivial
update (for example, one d_fillnew) incurs the full overhead of steps 1-7 above. As a
general rule, do as much as you can within one transaction. On the other hand,
remember that during the time between the d_lock and d_trend calls, other users are
prevented from using the same files. A transaction that is too long will cause uneven
response times in the overall system.
If a transaction is aborted, the modified pages in the page buffers are cleared.
An indexing technique has been developed to provide fast access to the changed
database pages that are stored in the log file after a cache overflow has occurred. It
utilizes a separate cache for the management of all database pages on the log file. The
size of this index cache (that is, the number of page buffers) is specified as an argument
to function d_setpages as follows:
d_setpages(dbpgs, ixpgs)
where dbpgs is an integer specifying the number of pages to be used for the standard
database cache and ixpgs is an integer specifying the number of pages to be used for the
overflow index cache. Both arguments are required on any call to d_setpages.
If your application is prone to large transaction overflows, you will want a fairly large
index cache (for example, 17 pages). Usually, however, a small number of index pages is
all that is necessary (for example, five pages, the default).
6.5
Database Recovery
If a program crashes while a transaction is active (after the d_trbegin but before a
transaction end or d_trend has been issued), the database is guaranteed to be consistent
since it has not been written to. Only the changes made since the beginning of the
transaction are lost. If the program fails while the modified pages are being written to
the log file (step 1 in section 6.4, above), the same situation exists. However, after the
changes have been written to the log file, if the program fails while the database is being
updated, recovery is possible. If the program fails while committing the database from
the log file, recovery is required.
Transaction Processing
6-5
6-6
On some networks (for example Novell) it may be necessary to create each log file prior
to running the first RDM program to ensure that they are shareable. The initial content
of the files is immaterial, since they are re-initialized when the database is opened. The
problem on Novell is that files created dynamically are always marked unshareable even
when requested as shareable and can only be made shareable through execution of the
Novell flag utility.
Whenever RDM initiates a recovery operation, the error reporter is called with the
message "recovery about to occur." The purpose of this function is simply to inform the
user that a recovery is about to take place. You will probably wish to tailor the error
reporting for your particular application requirements through d_set_dberr.
S_RECOVERY is probably one of the return codes that you will want to "ignore" in your
error reporter.
6.6
Archive Logging
Transaction Processing
6-7
Chapter 7
Multi-User Database Control
7.1
Introduction
In the example below, two users are attempting to update a record from the database at
about the same time. The left column gives a relative time and the other columns
identify the actions that occur for the respective user at each time.
Table 7-1. Two Users Updating a Record
Time
1
User 1
Read record
2
3
Read record
Modify record
4
5
User 2
Modify record
Write record
Write record
At Time 1, User 1 reads the record from the database. At Time 2, User 2 reads the record.
Both users then modify and write the record back to the database. But User 2s copy of
the record does not have User 1s changes (they were made after User 2 read the record).
Thus when User 2s record is written to the database, User 1s changes are lost. If User 2
had not read the record until after Time 5, the changes from User 1 would not have been
lost. But without any control there is no way to guarantee that User 2 will put off
reading until User 1s updates are completed.
The solution is to provide a mechanism whereby updates to shared data are
synchronized, so that only one user can be updating the shared data at a time. The
mechanism usually is some form of a lock, which is used to serialize updates to data
shared among multiple users. A lock must be applied before shared data is updated, so
that other users cannot update the locked data. Thus, through use of a lock, the above
example would proceed as shown in the following table:
7-1
User 1
Lock granted
Read record
Modify record
Write record
Free lock
User 2
Request record lock
Lock granted
Read record
Modify record
Write record
10
Free lock
Once User 1s lock request is granted at Time 2, User 2 will wait for the record to be
unlocked before continuing. When User 1 has completed the update, the lock is freed at
Time 6. User 2 is then granted the lock, and the record (which now includes User 1s
changes) is read and then updated with User 2s changes.
As the above example illustrates, the key issue in multi-user database applications is data
integrity, ensuring that no data is lost and that the data is logically consistent (that is, the
interdata relationships that should exist do exist). Data integrity is supported in RDM
through three interrelated facilities:
File and record locking
Transaction processing
Transaction logging and recovery
File locking and record locking are used, as in the example, to prevent loss of data by
synchronizing access to shared files and records so that only one user at a time can
update them.
Multi-user database application programming is a difficult task, which should be
approached with careful planning and design. The problems that can occur in multi-user
programs are often very difficult to resolve. The capabilities provided in RDM are
sufficient for you to ensure both data integrity and good performance. The remainder of
this chapter explains the RDM implementation of these capabilities, including guidelines
for their effective use.
7-2
7.2
Operational Environment
Lock Manager
Lock Requests
Lock Requests
Lock
Requests
USER:ALPHA
File I/O
USER:BETA
USER:GAMMA
File I/O
File I/O
USER:DELTA
File I/O
File Server
Database
Files
vista.taf
alpha.log
gamma.log
beta.log
delta.log
7-3
specified by the requesting process. The lock manager is a program that can be executed
from any node on a network or as a background task on a stand-alone machine. See the
RDM Multi-User Guide for a complete description of the lock manager.
A database-family transaction activity file (TAF) is used by the RDM runtime to control
database recovery in the event the lock manager goes down. This file is named vista.taf
by default, but can be named or located through either environment variable DBTAF or
function d_dbtaf, or the rdm.ini file, which must be called before d_open.
Each database family must use only one TAF so that automatic recovery is done
correctly. Also, all log files for the database family (TAF) must be located in the same
physical directory. The application must enforce these rules through the consistent use
of the environment variables, their corresponding d_ functions, and the rdm.ini file.
Multi-user database programs must open the database in shared access mode. This is
done by passing an open type of s to function d_open. For example, to open the tims
database for multi-user access, issue the following call:
if (d_open("tims", "s") == S_UNAVAIL)
{
printf("database unavailable\n");
exit(1);
}
Status code S_UNAVAIL is returned when some other database program has opened the
tims database in exclusive access mode.
7.3
File Locking
7.3.1 Introduction
The principal locking mechanism provided by RDM is the file lock. An advisory record
locking capability, which is used in conjunction with file locking, is also provided and is
described in section 7.5, "Advisory Record Locks." Although file locking is often
regarded as an inferior multi-user locking mechanism, in the RDM environment it can be
used very effectively without sacrificing performance. In fact, well-performing multiuser applications can and have been written using only file locking.
Much of the file locking functionality provided in RDM has been developed to optimize
multi-user performance. In addition, section 7.6, "Program Design Considerations," gives
some general program design guidelines to help you effectively use the RDM
capabilities. Table 7-3 lists the file locking functions.
7-4
Definition
d_recfree(REC, dbn)
d_setfree(SET, dbn)
d_keyfree(FIELD, dbn)
d_freeall()
d_timeout(secs)
d_locktimeout(readsecs,writesecs)
Note that functions d_trend and d_trabort are also used with file locking although they
are not listed here. Recall that both functions free locked files. The remaining pages of
this section describe the functions listed in Table 7-3.
Function d_reclock locks the data file containing the record type, as well as all key files
containing key fields defined in the specified record type. For example, assume the
following DDL statements appear in your schema:
7-5
The call d_reclock(INFO, "r", CURR_DB) will cause tims.d02 and tims.k01 to be readlocked. The call d_reclock(KEY_WORD,"r", CURR_DB) will cause tims.d01 and tims.k02
to be read-locked. Record locks are always necessary when creating a record with keys,
or when modifying key fields in records, because they guarantee that all of the files
related to the record will be locked.
Function d_setlock locks the data files that contain the owner and member record types
of the specified set. The call d_setlock(KEY_TO_INFO, "r", CURR_DB) will cause only
file tims.d01 to be read-locked, because both record types are contained in the same file.
This function does not lock key files associated with records. Set locks are necessary
when connecting or disconnecting records in sets, or when traversing sets, because they
guarantee that all of the files related to the set will be locked. Note, however, that if
record locks had already been applied to both the key_word and intersect record types,
the set lock would not be needed (although it would be accepted). Any time the correct
files have been locked, even though by a different locking call, functions that use the files
will execute without locking errors.
Function d_keylock locks the key file that contains the specified key field. The call
d_keylock(ID_CODE, "r", CURR_DB) will lock only file tims.k01. By locking only a key
file, you may scan keys, as in the following code:
7-6
By locking only the key file, you eliminate the overhead of obtaining locks on the data
file, and possibly other key files related to the same record type. Also, in a multi-user
environment, it is important not to lock any files that are not going to be used, because
you may be preventing other users from making progress.
Function d_lock is used to lock a group of record and set types, and is described in detail
in section 7.3.6, "Grouped Lock Requests."
Table 7-4 lists the types of locks that can be applied.
Table 7-4. File Lock Types
Type
Description
Read lock.
Write lock.
Exclusive lock.
Keep lock.
Functions d_recfree, d_setfree, and d_keyfree free read locks or exclusive locks on their
respective record, set, or key field types for their database. Function d_freeall will free
all read-locked files across all databases.
The current lock status of a record, set, or key type can be found by calling functions
d_reclstat, d_setlstat, or d_keylstat.
Function d_timeout is used to inform the lock manager of the number of seconds that
lock requests from this process are to wait on the queue before being denied.
Function d_locktimeout is used to tell the lock manager how long to allow read locks and
write locks to be held in the absence of communication from the runtime. This is
intended to prevent frozen clients from making the database permanently unavailable,
and works only with network lock managers (like TCP/IP).
7-7
Definition
d_recfrst(REC, dbn)
d_reclast(REC, dbn)
d_recnext(dbn)
d_recprev(dbn)
d_recread(&val, dbn)
Read locks can be requested either from outside or within a transaction. Read locks that
are issued within a transaction, or are not freed prior to a call to d_trbegin, are freed by
either d_trend or d_trabort (unless the lock is to be keptsee section 7.3.5,
"Upgrading/Downgrading of File Locks"). Read locks requested outside a transaction
are freed by calling the appropriate lock freeing function, or by the termination of an
intervening transaction.
7-8
7-9
2.
3.
4.
If the normal write lock request is denied, you should abort and restart the transaction as
usual. To upgrade a read lock to an exclusive lock, simply re-issue the d_reclock or
d_setlock with type x. The constraints that apply to upgrades from read locks to write
locks also apply to upgrades from read locks to exclusive locks.
To downgrade a write lock to a read lock, or to keep a record or set type read-locked after
execution of d_trend, re-issue a d_reclock or d_setlock call with type k prior to d_trend.
7-10
fact, deadlock-free programs result when all needed locks are requested at the beginning
of each transaction and processing only continues when all have been granted. This is
one of the standard techniques for avoiding deadlock.
Function d_lock provides this grouped lock request capability. The first argument
passed to d_lock is an integer containing the count of the number of items to be locked.
The second argument is a pointer to the packet of lock requests. Each lock request is
placed in a LOCK_REQUEST structure entry as defined below and declared in file
vista.h.
typedef struct {
int item;
/* number of record or set to be locked */
char type;
/* type of lock */
} LOCK_REQUEST;
Structure field item contains the record or set constant (as defined in dbname.h) for the
record or set to be locked. Key types cannot be locked using function d_lock. Field type
contains the type of lock to be applied: r, w, x, or k. (Note that these are single character
constants and not strings.)
Grouped lock requests can be statically defined. For example, suppose that, for
transaction trxc02, record types EMP and JOBS and set DEPT_EMPS are to be locked.
The following variables could be declared and used as follows:
LOCK_REQUEST trxc02[] = {
{EMP,
w},
{JOBS,
w},
{DEPT_EMPS, r}
};
int trxc02_count = 3; /* sizeof(trxc02) / sizeof(trxc02[0]) */
...
d_trbegin("trxc02");
if (d_lock(trxc02_count, trxc02, CURR_DB) == S_OKAY) {
... /* process transaction */
d_trend();
}
else
d_trabort();
The trade-off associated with using d_lock, as opposed to waiting until you actually need
the lock before you request it, is that the files are locked and others are prevented from
using them for a longer period of time. The actual impact will depend on how you
design your application program. The peace of mind associated with knowing your
program is free of deadlock is often worth some performance penalty.
7-11
specified set type. Function d_keylstat returns the current lock status of the specified
key field type. A pointer to a character variable that is to contain the lock status is
passed to each of the functions. Note that these functions only return the status of the
locks held by the requesting process.
7.4
Timestamping
7.4.1 Introduction
Consider the following RDM database access scenario:
d_reclock(REC, "r", CURR_DB);
d_keyfind(KEY, value, CURR_DB);
d_crget(&dba, CURR_DB);
/*
d_recfree(REC, CURR_DB);
/*
...
...
d_reclock(REC, "r", CURR_DB);
d_crset(&dba, CURR_DB);
/*
d_recread(REC, CURR_DB);
/*
...
The first four statements lock a record type, locate a record occurrence through a key
field, save the database address of that record, and then free the lock. The last three
statements re-lock the record type, set the current record to the saved database address,
7-12
and then read the record from the database. However, while the record type was
unlocked by this process another process may have updated the database and modified
or deleted the record associated with the saved database address. Thus, when the record
type is re-locked, the record occurrence is read directly through the database address,
which may now contain a deleted record (which would result in a database error) or a
modified record (in which case the user would not be made aware of the modifications).
One way to avoid this problem is to re-access the record with another d_keyfind, which
would return a "not found" status if the record had been deleted. However, this would
not reflect changes to the record, and it would be slower than a direct access through the
database address.
Timestamping is a technique whereby it is possible to detect whether a particular record
occurrence still exists, and if it has been modified since it was last accessed by a given
process. This allows direct access to a record after the file has been unlocked and
subsequently re-locked for those records that havent been updated in the interim.
7.4.2 Implementation
Each timestamped record occurrence contains two values in its record slot header. One
is the time when the record was created and the other is the time when the record was
last updated. When a record slot is accessed, the maximum of these two values (that is,
the time of last modification) is placed, along with the database address, in the currency
tables. Functions are provided that compare the value placed in the currency table
against the times stored in the record, in order to determine if the record has changed.
There may also be situations in which it is desirable to know if a set has changed since
last accessed. Thus, the time of last update value (connect or disconnect) is stored in the
timestamped set pointers, to be used to detect changed sets.
The time values cannot simply be the system clock time. In a network environment the
clocks of the different computers are not synchronized. What is used is a sequence
counter, which is accessible by all processes. This sequence counter is stored in the file
headers (page zero) for each database file, and is incremented each time a write lock is
placed on the file or the database is opened for exclusive access. The counter is
initialized to zero only when the database is initialized.
Timestamping is an optional feature enabled through the following DDL specifications:
timestamp records [ recname [, recname... ] ] ;
timestamp sets [ setname [, setname... ] ] ;
These statements may appear anywhere within a DDL schema specification file. If no
record or set names are specified, all records or sets will be timestamped.
7-13
Record timestamping adds eight bytes of additional space overhead to each record slot.
Set timestamping adds four bytes of additional space overhead to each set pointer
contained in set owner records.
The RDM runtime support for timestamping includes the following:
Last access timestamps, incorporated with the currency tables
Currency table access functions to handle the last access timestamps
Functions that test the timestamp status
If timestamping is enabled for a record, the currency table will associate the most recent
modification time of that record with its database address. Whenever the currency of
that record changes (current owner, current record, or current set) through the
navigation functions, that modification time will be adjusted. These timestamp values
are then used by the five functions listed in Table 7-6 to test the modification status of
that record.
Table 7-6. Timestamp Status Functions
Function
Definition
d_crstat(dbn)
d_costat(SET, dbn)
d_cmstat(SET, dbn)
d_csstat(SET, dbn)
Each of the above functions returns one of the status codes listed in Table 7-7 below.
Table 7-7. Timestamp Status Codes
S_OKAY
S_UPDATED
S_DELETED
Status S_DELETED is returned if the record slot contains either a deleted record, or a
record with a creation date that is later than the current last access timestamp. Status
S_UPDATED is returned if the record or set has been updated since it was last accessed.
The functions listed in Table 7-8 provide the ability to get and set the last access (or
currency) timestamp values stored with the timestamps. All timestamp variables are of
type "unsigned long."
7-14
Definition
d_gtscr(&ts, dbn)
d_stscr(ts, dbn)
Also call
d_crget
d_gtscr
d_csoget
d_gtsco, d_gtscs
d_csmget
d_gtscm
d_crset
d_stscr
d_csoset
d_stsco, d_stscs
d_csmset
d_stscm
Using these capabilities, the example given in the introduction could be coded as follows:
7-15
DB_ADDR dba;
ULONG rts;
...
d_reclock(REC, "r", CURR_DB);
d_keyfind(KEY, value, CURR_DB);
d_crget(&dba, CURR_DB);
/*
d_gtscr(&rts, CURR_DB);
/*
d_recfree(REC, CURR_DB);
/*
...
...
d_reclock(REC, "r", CURR_DB);
Status code S_TIMESTAMP is returned when the record or set involved in the function is
not timestamped. Status S_NOTLOCKED is returned when the record or set type
involved in the function is not locked.
7-16
7.5
Three functions that support the locking of individual record occurrences are given in
Table 7-10. These functions are used in conjunction with file locking to support end-user
modification of data without having to keep a file locked.
Table 7-10. Record Locking Functions
Function
Definition
d_rlbset(dbn)
d_rlbtst(dbn)
d_rlbclr(dbn)
A bit in the record is used as a flag that indicates whether the record occurrence is locked.
This bit is called the record lock bit. The record locking functions are used to set, test,
and clear the record lock bit of the current record.
Function d_rlbset is called to set the record lock bit of the current record. The data file
containing the record need not be locked. The RDM runtime will automatically
synchronize access to the record. The current record is accessed directly, without using
the cache, so that upon successful completion of the call the bit is physically set in the
record on the disk. Function d_rlbset will return status S_UNAVAIL if the system
cannot gain access to the data file (due to its being write or exclusively locked). Status
S_LOCKED is returned if the current records lock bit is already set. Status S_OKAY is
returned when the function has successfully set the lock bit. If d_rlbset is called within a
transaction, the operation still goes directly to the file, bypassing use of the cache. Thus,
a d_trabort call does not undo any record lock bit settings that were performed within
the transaction.
Function d_rlbclr is called to clear the record lock bit of the current record. After a
record occurrence is locked, the record will remain locked until function d_rlbclr is called
to clear the lock bit. If d_rlbclr is called from within a transaction, the data file
containing the current record must be write-locked. User error S_NOTLOCKED is
returned if the function is called within a transaction and the data file of the current
record is not write-locked. The clearing of the record lock bit cannot be applied until the
transaction ends because other modifications to the record are usually involved in the
transaction. If d_trabort is called after d_rlbclr, the clear is aborted and the record
remains locked. When d_rlbclr is called within a transaction, the lock bit is cleared
through the cache rather than directly to the file. If d_rlbclr is called outside of a
transaction, the file does not need to be locked and the operation will directly clear the
lock bit on the file. This is used to clear a lock after it has been determined that the
record no longer needs to be updated.
Multi-User Database Control
7-17
Function d_rlbtst can be called to test the lock bit status of the current record. Status
code S_LOCKED is returned if the lock bit of the current record is set; code
S_UNLOCKED is returned if the lock bit is clear. The data file containing the current
record does not need to be locked in order to call d_rlbtst.
In addition to the above functions, the system provides a global integer status variable in
the single-tasking, static-link libraries called rlb_status, which is set to S_LOCKED or
S_UNLOCKED by the following record read functions:
d_recread
d_crread
d_csoread
d_csmread
No records that have been locked by the program should remain locked after the
database is closed. The Database Consistency Check utility, dbcheck, can be used to
report all record occurrences that have the lock bit set, or the utility dbclrlb can be used
to clear them.
Note: The setting of a records lock bit (RLB) will not automatically prevent another
process from updating the locked record. It is the responsibility of the application to
check if a record occurrence is locked before allowing updates to it, by either calling
d_rlbtst or checking rlb_status after reading the records contents. The RDM functions
that modify the database do not use the status of the RLB.
Use of these functions will be illustrated in the next section.
7.6
7-18
The design techniques that follow show how these principles are applied in data entry,
deletion, modification, and retrieval.
2.
Begin transaction.
3.
4.
5.
End transaction.
The goal is to minimize the amount of time that a file is locked. In this procedure, the
files are locked for only the amount of time it takes to perform Steps 4 and 5. If the time
required to perform those steps is great (more than a few seconds), it may be beneficial to
try to divide the transaction into two or more smaller ones. In general, however, this will
not be required. A segment of the ent_info code from Chapter 5, "Database
Manipulation," is given below with multi-user control functions added.
7-19
int info_cnt = 5;
LOCK_REQUEST info_pkt[] = {
{AUTHOR_LIST, w},
{INFO,
w},
{ABSTRACT,
w},
{INTERSECT, w},
{KEY_WORD,
w}
};
...
/* Enter technical information records into TIMS database */
ent_info()
{
char s[32]; /* generic string variable */
/* enter tech info into TIMS database */
while (get_info() != EOF)
{
d_trbegin("ent_info");
while (d_lock(info_cnt, &info_pkt, CURR_DB) == S_UNAVAIL)
; /* wait until locks are available */
if (db_status != S_OKAY)
{
d_trabort();
... /* notify user of problem */
return (0);
}
/* see if author exists */
...
/* create new tech. info record */
if (d_fillnew(INFO, &irec, CURR_DB) == S_DUPLICATE)
{
d_trabort();
printf("duplicate id_code: %s\n", irec.id_code);
return;
}
else
{
/* connect to author record */
...
enter_key_words();
enter_abstract();
}
d_trend();
}
}
In the info_pkt lock request packet, one set type and four record types are locked, even
though five set types are involved in the transaction. The RDM runtime system does not
care how a file lock is obtained, as long as the appropriate files are locked when the
operation requiring the lock is performed. The data files involved in the set operations
are locked when the record types are locked. The lock packet could have listed every set
type and every record type involved in the transaction, but this would make it larger
than necessary. (RDM does optimize the lock requests so that a lock request for a given
file is made only once). The d_lock call is repeated until the file locks are granted. This
7-20
cannot produce a deadlock in this situation since no other files are locked. Notice that if
the id_code is a duplicate, the transaction is aborted.
In this case, however, another data entry guideline has been violated, which is that files
should not be kept locked while control has been given to the user. Functions
enter_key_words and enter_abstract both prompt the user for information while the files
are locked. To properly implement the function, it would be necessary to redesign the
program so that the user first enters all of the needed information (that is, have get_info
also prompt for the key words and abstract), after which the transaction is processed.
2.
3.
4.
Free files.
5.
6.
Begin transaction.
7.
8.
9.
End transaction.
Steps 2 through 5 are optional. They should not occur between Steps 7 and 8, however,
because of the confirmation request. In this procedure the files are read-locked for the
duration of Step 3 and write-locked for the duration of steps 8 and 9. In Step 2, read
locks are necessary if the data is to be accessed by key or by set. If it can be accessed
directly (for example, d_recread), the file need not be read-locked at all.
Function del_info from Chapter 5, "Database Manipulation," illustrates the previous
procedure.
7-21
int info_cnt = 5;
LOCK_REQUEST info_pkt[] = {
{AUTHOR_LIST, w},
{INFO,
w},
{ABSTRACT,
w},
{INTERSECT, w},
{KEY_WORD,
w}
};
del_info()
{
struct info irec;
long count;
char id[16], name[32];
printf("id_code: " ); /* get info to delete */
gets(id);
d_reclock(INFO, "r", CURR_DB);
if (d_keyfind(ID_CODE, id, CURR_DB) == S_NOTFOUND)
{
d_recfree(INFO, CURR_DB);
printf("id_code %s not on file\n", id);
return;
}
d_recread(&irec, CURR_DB);
d_reclock(AUTHOR, "r", CURR_DB); /* get name */
d_findco(HAS_PUBLISHED, CURR_DB);
d_crread(NAME, name, CURR_DB);
d_recfree(INFO, CURR_DB);
d_recfree(AUTHOR, CURR_DB);
...
/* display data and confirm delete request */
d_trbegin("del_info");
while (d_lock(info_cnt, &info_pkt, CURR_DB) == S_UNAVAIL)
;
/* wait till locks are available */
/* disconnect and delete abstract */
...
/* disconnect, delete intersect and key word */
...
/* disconnect info rec from author, delete */
...
/* delete author, if no other pubs records */
...
d_trend();
}
7-22
6.
7.
8.
9.
Begin transaction.
Write-lock necessary set and record types.
Perform necessary modifications.
End transaction.
Here again, no files are locked while the user is editing the data. But notice that since the
files are not locked, it is possible that some other process may try to modify the same
data at the same time. In many cases, however, even though it may be theoretically
possible, it is not practically possible. The nature of the application may render it
impossible for two users to modify the same data at the same time. For example, in the
tims database it isnt possible for the same book to be returned by more than one
borrower at the same time. In designing your application you should carefully analyze
the real-world use of your data and avoid incorporating unnecessary code to handle
impossible circumstances.
However, there are many situations where it is possible for two users to attempt to
modify the same data at the same time. Record locking is used to synchronize these
updates, as in the following procedure:
1.
2.
3.
4.
5.
Begin transaction.
6.
7.
Modify record.
8.
9.
End transaction.
7-23
2.
Browsing through a list of records where there may be one record per line
on the display.
3.
Producing reports where many records in the database may need to be accessed.
For Type 1 data retrieval in RDM, the procedure is just to lock the necessary record, set,
or key types, then access the data, free the locks, and display the results. For example,
the following code displays an item by id code from the tims database:
7-24
Since the info record is accessed by id_code, d_keylock is used to lock only the key file
containing id_code keys. Function d_reclock could also be used to lock the data and key
files associated with info. In this case it would make no difference which one you used.
In general, however, d_reclock may lock key files that are not involved in the operation
(since it locks all key files and some may not be used). The d_setlock call locks the data
files containing the owner and member record types of set HAS_PUBLISHED, which in
this case is simply tims.d02 (see tims DDL specification in section 4.5.3, "Database
Design"). Notice that the file locks are freed as soon as they are no longer needed.
Data retrieval of Types 2 and 3 use the following procedure:
1.
2.
3.
Free locks.
4.
5.
Go to step 1.
With interactive browsing, it is imperative that the locks be freed at Step 3. Otherwise
the duration of the locks would be dependent on the end-user, who typically would
press some key to view the next page of data.
In a report, the data is printed in Step 2, and Step 4 is eliminated. Although the locks
could remain in place for the duration of the report, this would prevent any updates to
the locked files until the report completes. This procedure allows updates by iteratively
freeing and re-locking the files during the report. The duration of the locks should
remain reasonably small (for example, access 10 to 20 records each time). The following
7-25
code will print a report of all info records contained in the tims database in id_code
order:
struct info irec;
int i;
for ( i = 0; ; )
{
if (i == 0)
d_reclock(INFO, "r", CURR_DB);
if (d_keynext(ID_CODE, CURR_DB) != S_OKAY)
{
d_recfree(INFO, CURR_DB);
break;
}
d_recread(&irec, CURR_DB);
printf("id_code: %-20s title: %s\n", irec.id_code, irec.info_title);
if (++i == 20)
{
d_recfree(INFO, CURR_DB);
i = 0;
}
}
The lock on the info record type will be freed every 20 records, allowing any queued
write-lock requests access to the locked files.
This procedure is slightly more complicated when a set is scanned instead of a key. The
problem occurs when an update is made to the set occurrence being scanned while the
set lock is freed. If the current member at the time of the d_setfree is disconnected, then
when the scan continues it will result in an error detected by RDM. If the set is
timestamped, its status can be checked after the set type is locked to determine whether
the set has been updated. If so, the operation can be terminated or restarted. This will
often be acceptable when there are many occurrences of the scanned set type. If it is a
system-owned set, however, there is only one occurrence, and if updates are common,
timestamping the set will not help. In this situation, use of a record lock on the current
member can be used to keep it from being disconnected. This is illustrated in the
following code, which produces a report of authors:
7-26
The d_csmset call makes the current member of author_list null, so that the first call to
d_findnm will return the first member. Integer i starts at -1 to indicate that there is no
current record lock to be cleared. When 20 records have been scanned, d_rlbset is called
to set the record lock of the current record (which is also the current member). If the lock
bit is already set (perhaps someone is updating it), i is decremented so that the file lock is
not freed until the current member can be locked. In order to work correctly, an author
record should not be disconnected from the author_list set if its record lock bit is set.
7.7
The multi-user version can be used in two different one user modes. As with earlier
versions, a database can be opened in exclusive access mode by passing open type x as
7-27
the second argument on the d_open call. A "one user-only mode" is selected by passing
open type o to d_open.
Exclusive-access mode requires the presence of the lock manager. If no other users have
opened the selected database, the lock manager will grant exclusive access to the
database and will prevent any other access to that database until it has been closed.
Exclusive access is intended for use in a multi-user environment to update static tables or
perform other critical update operations (for example, a year-end purge).
One user-only access does not require the presence of the lock manager. Even if the lock
manager is present, no interaction will occur. In one-user mode, it is the applications
responsibility to make sure that one and only one user is accessing that database at a
time. This mode is intended for use in a one user environment where: 1) the transaction
processing and recovery features in the multi-user version are needed in a one user
application, or 2) a one user version of a multi-user application is to be distributed and a
single copy of the source is desired.
A program running in either one user or exclusive mode does not need to place any locks
on any files. Any calls to the lock functions will immediately return (with no error).
However, if transaction processing and logging are used, file locking will be fully
operational, except that the runtime cache will never need to be cleared.
7-28
Chapter 8
Interactive Database Access Utility
(ida and wida)
Centura RDM includes two interactive database access utilities: the ida utility, for
working with RDM databases running on non-Windows platforms, and wida, for
working with RDM databases running on a Windows platform. This chapter provides
information about using the ida utility in section 8.1 and information about using the
wida utility in section 8.2.
8.1
Using ida
ida has been designed for application developers and database administrators and
therefore is not suitable for most non-technical users.
8.1.1.1
Accessing ida
2.
Use the cd command to change to the directory that contains the database dictionary
file (dbname.dbd) and the data and key files. (For further information, see the note
that follows this procedure.)
8-1
3.
(For information about special command line options for opening ida, see Chapter 2
of the RDM Reference Manual.)
4.
When the ida title page appears, press any key to bring up the ida main menu.
Note: Usually, ida is executed from within the file directory that contains the database
dictionary file (dbname.dbd) and the data and key files. However, you can use the
Parameters Dictionary_path and Parameters Files_path commands from the Utility
menu to locate the database files before opening the database. Alternatively, you can use
the environment variables DBDPATH and DBFPATH to tell ida where the database files
are located, or you can check the rdm.ini file. See section 5.2.2, "Operational
Environment," for a complete discussion of RDM path environment variables.
8.1.1.2
Fig. 8-1 shows the first ida menu, referred to as the main menu. This menu provides
access to all other ida menus.
IDA - RDM Interactive Database Access Utility
Open Access Initialize Close Parameters Quit
Open a RDM database
Menu title
2nd line
3rd line
You can see what a command does by moving the highlight to that command. When a
command is highlighted, the description of that command appears on the 3rd line.
To leave the ida utility, you use the Quit command from the main menu. For more
information about using the commands on the ida main menu, including the Quit
command, see section 8.1.2.
8-2
8.1.1.3
This section describes the concepts and procedures you will need in order to use the ida
utility. These concepts and techniques include messages from ida, executing a command
from an ida menu (from the 2nd line of the screen), choosing an option from a numbered
list (displayed below the top three lines), moving the highlight on a numbered list,
opening a database, accessing a submenu, exiting a submenu, and selecting a record to
modify.
Author
INFO
BORROWER
INFOTEXT
KEY_WORD
INTERSECT
SYSTEM
8-3
Selection lists that extend beyond a single screen will either scroll or redisplay,
depending on the position of the highlighted selection.
Opening a Database
Although you can access ida and view the main menu, there is little you can do until you
open a database. (You can execute the Parameters and Quit commands, but the Access,
Initialize, and Close commands will not work without an open database.) If you are
new to ida, you can use the sample tims database (provided with RDM) to learn any of
the procedures described in this chapter.
To open a database:
1.
From the ida main menu, highlight and execute the Open command.
2.
On the Open a RDM database submenu, execute the command for the type of user
access you want. (If you are just learning to use ida, use the One_User command.)
3.
From the Select Database list, choose the database you want to open. When you
choose a database, the list disappears.
8-4
Accessing a Submenu
To perform most database operations, you will use the Access command on the main
menu. Selecting the Access command displays the Database Access Commands
submenu shown in Fig. 8-3.
Database Access Commands
Record Set Currency Transaction
Lock
Free
Miscellaneous
X_exit
Exiting a Submenu
When you exit a submenu, you go back to the previous menu. There are two ways to exit
a submenu:
On the 2nd line of the submenu, highlight the X_exit command and press <Enter>.
or
Press the <Esc> key.
Selecting a Record
Using ida, you can find a record by using the Keyscan command to find a keyword, or
you can use the Recscan command to find a record by using a database address. The
following procedure uses the Recscan command.
To select a record using Recscan:
1.
2.
Recscan
X_exit
From the Record Manipulation Functions submenu, highlight and execute the
Recscan command.
The Scan and view records based on database address submenu appears (shown in
Fig. 8-2).
4.
From the Scan and view records based on database address submenu, highlight and
execute the Scan command.
8-5
5.
A numbered list titled SELECT RECORD TYPE displays. Type the number of the
record type you want to select.
The Scan and View Records submenu appears and displays a record (shown in
Fig. 8-5).
Scan and View Records
Next First Select X_exit
Display next page of records
RECORD:
AUTHOR
Page:
RECORD CONTENTS
1.
{"Martin, James"}
2.
{"Myers, G."}
3.
{"Teorey T. & Fry, J."}
Highlight and enter the Select command. The highlight will move to the numbered
list.
7.
Choose an option from the numbered list. The record will be displayed in the ida
screen.
Open command
The Open command opens a database. If another database is currently open, it will first
be closed. After selecting the Open command, you must choose the One_User, Shared,
or Exclusive access option from the displayed submenu. A list of the databases
contained in the current (or DBDPATH) directory is then displayed. From this list, you
can select the database to be opened.
Access command
The Access command brings up the Database Access Commands submenu, which
provides a set of functions and submenus for manipulating a particular database. This
menu, described in section 8.1.4, includes record functions, set functions, currency
functions, transaction functions, and lock functions. The Access command may only be
called after a database has been opened.
8-6
Initialize command
This command, which corresponds to the d_initialize function, initializes the opened
RDM database. This command should only be used after a database has been opened for
exclusive access. It will destroy the contents of the database.
Close command
The Close command closes an opened database.
Parameters command
Executing the Parameters command accesses the Set RDM Operational Parameters
submenu. It also displays the current ida operational settings.
The Set RDM Operational Parameters submenu is shown in Fig. 8-6, followed by a
description of each of its commands. These commands are used to change the RDM
settings and cannot be used when a database is open.
Set RDM Operational Parameters
Dictionary_path Files_path Userid Pages Max_files Logging Chain X_exit
8-7
Logging
Toggles the transaction logging option. The screen will indicate whether transaction
logging is on (enabled) or off (disabled).
Chain
Controls the use of the delete chain for storing newly created records. The display
will show whether delete chain usage is on (enabled) or off (disabled).
Quit command
Closes any opened database and ends the ida session. The environment variables
DBDPATH and DBFPATH are restored to their initial values.
8.1.3.1
To access the Display/Edit submenu, use the procedures in section 8.1.1.3 to:
Select a record from the database. (Even if you want to enter a new record into the
database, you will need to select an existing record in order to access the
Display/Edit Record submenu.)
Exit (back out of) the Scan and view records submenu to the Record Manipulation
Functions submenu.
From the Record Manipulation Functions submenu (Fig. 8-4), select Modify to
access the Display/Edit Record submenu.
8-8
Display/Edit Record
Mode: COMMAND
Edit Init Next Prev Write Store_key Delete_key Owner Connect X_Exit
Edit record
FIELD: ID_CODE
EDIT:>sw001<
ID_CODE
INFO_TITLE
PUBLISHER
PUB_DATE
INFO_TYPE
SIZE: 16
>sw001<
>Software Reliability<
>Wiley<
>1976<
>0<
8.1.3.2
Edit command
Selecting this command invokes field-level editing of the displayed record. The field edit
commands are listed in Table 8-2. The first field is highlighted and displayed in the data
field edit area. Field editing is initially entered in Command mode. For information
about ida editing modes, see section 8.1.3.4.
Init command
Use this command to initialize a new record. Selecting this command clears the
displayed record and enters field editing (see section 8.1.3.4) in Insert mode.
Next command
If the displayed record type consists of more data fields than can be shown on one screen,
this command can be used to display the next page of fields. Note that when entering
data, each page of data fields must be entered before the record is stored. Use the Next
command to access each page.
8-9
Prev command
Selecting this command displays the previous page of data fields. Each page of data
fields must be entered before the record is stored.
Write command
Selecting this command writes the entered or modified record to the database. If the
automatic set connection feature is enabled, then the record will also be connected to
each set of which it is a member and for which a current owner has previously been set.
Store_key command
This command is used to store optional keys in the key file. A selection list of optional
keys will be displayed. Select the one to be stored for the current record.
Delete_key command
This command is used to delete optional keys from the key file. A selection list of
optional keys will be displayed. Select the one to be deleted from the current record.
Owner command
This command is used to assign, from the current record, the current owner of the
selected set. It is provided here to facilitate data entry.
Connect command
Selecting this command connects the current record to the set selected from the displayed
list of sets. It is provided here to facilitate data entry.
8.1.3.3
In the Display/Edit Record submenu, selecting the Edit or the Init command allows you
to edit a record one data field at a time. ida editing incorporates many of the UNIX vi
editor commands (see Table 8-2).
All editing is performed in the editing field. Just above the editing field, the name of the
field being edited is displayed. If this field already contains data, the data will be
displayed in the editing field. Arrows on either end of the editing field point inward if
all the data in the field fits on the screen, or outward in the direction where more data
can be found. The entire contents of the field are accessed by horizontally scrolling or
repositioning through the field, using cursor-positioning field edit commands.
You can move to different data fields by using the up and down arrow keys, or by
pressing <Enter> or <Tab> to move to the next field. Pressing <Enter> or <Tab> on the
last field, or pressing <Esc> at any point, will return you to the Display/Edit Record
submenu.
8-10
The format of the displayed data is similar to the format that would be used if the data
were statically initialized in a C variable declaration. Each item of an arrayed (nonstring) data field is separated by commas and specified in its natural sequence. Structure
fields must be enclosed by braces ({ }). Character strings do not need to be delimited by
quotation marks except when included as part of a struct field. Sub-fields of struct fields
cannot be edited independently, and they are displayed only in the struct field. All C
escape codes (for example, \n, \f, \r, \ddd) can be entered into character fields.
One-dimensional character arrays are always treated as strings, terminated by a null byte
(\0). Multi-dimensional character arrays will display the entire length of the field.
Fields of type DB_ADDR are displayed and entered as [f :r], where f is the file number
and r is the record number. As an example, [3:11717] is the database address of the
record located at slot number 11717 in file number 3.
Repeating data items in arrayed data fields are indicated with a d* prefix, where d is the
decimal number of repetitions, and "*" indicates a repeating field.
The following table lists some examples of the above requirements.
Table 8-1. Example Data Field Contents
Field Spec
Display
char str[255];
int array[10];
struct {
char name[15];
int age;
} children[7];
DB_ADDR tree[2];
char lines[10][10]
8.1.3.4
>[2:1377],[2:1381]<
>line 1\n\0\0\0line 2\n\0\0\0line3\n\>
The current editing mode is shown in the upper right corner of the screen. There are
three modes:
Command mode indicates that the field edit commands are enabled. Table 8-2 lists
the edit commands available in Command mode.
8-11
Move the cursor forward one "small" word, delimited by anything other than
letters, digits, or underscores.
f<c>
F<c>
;
/<s>
Delete between cursor position and the position determined from the cursor
position command that immediately follows. For example, "df." will delete
from the next occurrence of character <c> to the right from the cursor
through the next ".".
8-12
Function Key
<Left Arrow>
<Right Arrow>
<Home>
<End>
<Ins>
<Del>
<F1>
<F2>
<F3>
<F4>
<F5>
<F6>
<F7>
<F8>
<F9>
<F10>
Record command
Section 8.1.4.2
Set command
Section 8.1.4.3
Currency command
Section 8.1.4.4
Transaction command
Section 8.1.4.5
Lock command
Section 8.1.4.6
Free command
Section 8.1.4.7
Miscellaneous command
8-13
8.1.4.1
The Record command, selected from the Database Access Commands submenu,
accesses the Record Manipulation Functions submenu, from which you can select a set
of additional commands and submenus for managing records. This submenu is shown
in Fig. 8-9, followed by a description of each of its commands and their submenus.
Record Manipulation Functions
Keyscan Get Enter Modify Delete
Recscan
X_exit
Keyscan command
Selecting the Keyscan command accesses the Scan and View Record Based on Key
submenu, from which you can choose how to locate a record based on a key. This
submenu is shown in Fig. 8-10, followed by a description of its commands.
Scan and View Record Based on Key
Scan Keyfind First Last Next Previous
X_exit
8-14
Key: EMP_ID
Page: 1
8-15
Get command
Displays the record located at a specified database address. When prompted for the
database address, enter it as:
enter database address: [f:r]
where f is the file number and r is the record number. Refer to section 14.2.2, "Data File
Organization," for a description of database addresses.
Enter command
Enters new records into the database. From the displayed list, select the record type to be
entered. The Display/Edit Record submenu is then invoked. Section 8.1.3.2 shows the
Display/Edit Record submenu and describes its functions in detail.
Modify command
Invokes the Display/Edit Record submenu, which provides a set of tools for modifying
the current record. For detailed information about the Display/Edit Record submenu,
see section 8.1.3.2.
Delete command
Deletes the current record from the database. The record must have been previously
removed from all sets of which it was an owner or a member.
Recscan command
Invokes the Scan and View Record Based on Database Address command submenu.
The commands in this submenu use the d_recfrst, d_recnext, d_reclast and d_recprev
functions to locate the records, instead of the key traversal functions.
Scan and View Record Based on Database Address
Scan First Last Next Previous X_exit
Fig. 8-12. Scan and View Record Based on Database Address Submenu
Scan
Scans records based on their database address (their order is determined by their
physical appearance in a data file). A list of all record type names will be displayed,
from which you should select the type to be scanned.
The records located by the scan will be paged and displayed in the same horizontal
packed format as the Keyscan command. For more information about the display of
various RDM data types, see section 8.1.3.3.
8-16
First
Finds and displays the first record of the selected type in the data file.
Last
Finds and displays the last record of the selected type in the data file.
Next
Finds and displays the next record in the data file. When it reaches the end of the
file, it displays a "no more records" message.
Previous
Finds and displays the previous record in the data file. When it reaches the top of the
file, it displays a "no more records" message.
8.1.4.2
Selecting the Set command from the Database Access Commands submenu accesses the
Set Manipulation Functions submenu, which provides a set of tools for managing sets.
This submenu is shown in Fig. 8-13, followed by a description of its functions.
Set Manipulation Functions
Scan Owner First Next Last Prev Connect Discon Total X_exit
Scan command
Scans and displays all member records connected to the current owner of a set. The set
to be scanned is selected from the displayed list of set names.
The records located by the scan will be paged and displayed in a horizontal packed
format. This is similar to the format that would result if the struct variable
corresponding to the record were statically initialized in a C program. That is, it would
be enclosed in braces ({ }), with each sub-field grouping separated by commas. For more
information about the display of various RDM data types, see section 8.1.3.3.
Owner command
Finds the owner of the current record, through the set selected from the displayed list of
set names. Note that this function serves a different purpose than the Owner function
under the Display/Edit Record submenu.
First command
Finds the first member connected to the current owner of the set selected from the
displayed list of set names.
8-17
Next command
Finds the next member of a set. The first time (or after a "no more records" message),
Next displays the first member of the set.
Last command
Finds the last member connected to the current owner of the set selected from the
displayed list of set names.
Prev command
Finds the previous member of a set. The first time (or after a "no more records" message),
Prev displays the last member of the set. A Last or Next command must have preceded
the call to Prev.
Connect command
Connects the current record to the current owner of the set selected from the displayed
list of set names.
Discon command
Disconnects the current record from the set selected from the displayed list of set names.
Total command
Displays a count of the members connected to the current owner of the set selected from
the displayed list of set names.
8.1.4.3
The Currency command, selected from the Database Access Commands submenu,
accesses the Currency Table Manipulation Functions submenu (shown in Fig. 8-14 and
followed by a description of its functions).
Currency Table Manipulation Functions
Owner Member Record Auto_set Display
Timestamp
X_exit
Owner command
Sets the current owner of the set selected from the displayed list of set names. This
function then invokes the Change Current Owner of Set submenu, from which you
select the record you wish to be made current. The submenu is shown in Fig. 8-15,
followed by a description of its functions.
8-18
X_exit
where f is the file number and r is the record number. Refer to section 14.2.2, "Data
File Organization," for a description of database addresses.
Member command
Sets the current member of the set selected from the displayed list of set names. It then
invokes the Change Current Member of Set submenu, shown in Fig. 8-16, to determine
the record to be made current.
Change Current Member of Set
Record Owner Member Change
X_exit
8-19
Change
Changes the current member of the selected set to the database address entered in
response to the prompt:
enter database address: [f :r]
Record command
Changes the current record. This selection invokes the Change Current Record
submenu, shown in Fig. 8-17, to determine the record to be made current.
Change Current Record
Owner Member Change
X_exit
Auto_set command
Toggles the automatic set connection flag within ida. When turned on, this flag causes
newly entered records to be connected automatically to the current owners of the sets for
which they are members.
Display command
Displays the currency tables, showing the record type and database addresses of the
current record and, for each set, the current owner and current member of that set. It also
indicates whether automatic set connection is enabled.
8-20
Timestamp command
Invokes the Timestamp Currency Functions submenu, shown in Fig. 8-18, for testing
timestamp statuses in the currency table.
Timestamp Currency Functions
Record Owner Member Set X_exit
8.1.4.4
The Transaction command, selected from the Database Access Commands submenu,
controls the initiation and termination of transactions. It invokes the Transaction
Processing Functions submenu, shown below, from which a transaction can be started
(Begin), ended (End), or aborted (Abort).
Transaction Processing Functions
Begin End Abort X_exit
Begin command
Starts a transaction. You are prompted to enter the transaction identifier.
End command
Stops a transaction. It applies to the database files all changes made since the beginning
of the transaction.
8-21
Abort command
Aborts the current transaction. It discards all changes made since the beginning of the
transaction.
8.1.4.5
The Lock command, selected from the Database Access Commands submenu, is used to
lock set types, record types, and key types, as well as the current record. It invokes the
Multiuser Set/Record Lock Functions submenu, shown in Fig. 8-20, from which you
select either record, set, key, or current locks. You then select the record, set, or key to be
locked from the displayed list of record, set, or key names defined in the database.
Multiuser Set/Record Lock Functions
Record Set Key Current Timeout
Display
X_exit
Record command
Places a lock on the record type selected from the displayed list of records. A selection
list of lock types will be displayed, from which you should select the desired lock type.
This command can only be executed when the database is opened for shared access.
Set command
Places a lock on the set type selected from the displayed list of sets. A selection list of
lock types will be displayed, from which you should select the desired lock type. This
command can only be executed when the database is opened for shared access.
Key command
Places a lock on the key type selected from the displayed list of keys. A selection list of
lock types will be displayed, from which you should select the desired lock type. This
command can only be executed when the database is opened for shared access.
Current command
Sets the record lock bit of the current record.
Timeout command
Displays current timeout value in seconds and prompts user for the new value.
8-22
Display command
Displays the lock status for each set, record, and key type, indicating whether it is readlocked, read-locked with keep, write-locked, exclusively locked, or free. This command
can only be executed when the database is opened for shared access.
8.1.4.6
The Free command, selected from the Database Access Commands submenu, is used to
free locked record, set, and key types, or to clear the current records lock bit. Free
invokes the Multi-user Set/Record Free Locks Functions submenu, shown in Fig. 8-21.
From here, you select which locks you want freed. If you are freeing a set, record, or key
lock, select the correct one from the displayed list of names defined in the database.
Multi-user Set/Record Free Locks Functions
Record Set Key All Current X_exit
Record command
Frees the lock for the record type selected from the displayed list of record types. This
command can only be executed when the database is opened for shared access and when
no transaction is active.
Set command
Frees the lock for the set type selected from the displayed list of set types. This command
can only be executed when the database is opened for shared access and when no
transaction is active.
Key command
Frees the lock for the key type selected from the displayed list of key types. This
command can only be executed when the database is opened for shared access and when
no transaction is active.
All command
Frees all set, record and key locks. It can only be executed when the database is opened
for shared access and when no transaction is active.
Current command
Clears the lock bit of the current record.
8-23
8.1.4.7
The Miscellaneous command, selected from the Database Access Commands submenu,
invokes the Miscellaneous ida Functions submenu, shown in Fig. 8-22, for displaying
current database file and lock statuses.
Miscellaneous ida Functions
Files Locks X_exit
Files command
Displays status information pertaining to the database and all database files, as shown in
Table 8-4.
Locks command
Displays the lock status for each set, record, and key type, indicating whether it is readlocked, read-locked with keep, write-locked, exclusively locked, or free. This command
can only be executed when the database is opened for shared access.
Table 8-4. File Status Information (From the Miscellaneous Files Command)
Item Name
Database name
Description
The name of the database currently being accessed.
Access type
Transaction
FILE
SLOTS
The total number of allocated file slots (if data file) or pages (if key
file).
TYPE
STATUS
RW
The total number of read locks on the file within this application. A
file may be locked from different set/record locks. This value gives
the current total. It is displayed only when the database is opened
for shared access. A value of -1 indicates that the file is writelocked.
EX
NAME
8-24
d_setpages
Parameters Pages
d_setfiles
Parameters Max_files
d_dbdpath
Parameters Dictionary_path
d_dbfpath
Parameters Files_path
d_dbuserid
Parameters Userid
d_on_opt
Parameters Logging/Chain
d_off_opt
Parameters Logging/Chain
d_open
Open
d_close
Close
d_initialize
Initialize
d_initfile
d_destroy
d_recread
d_recwrite
d_fillnew
d_makenew
d_setkey
d_delete
d_keystore
d_keydel
8-25
Table 8-5. RDM Functions and Corresponding ida Command Sequences (continued)
Function
d_connect
d_discon
d_members
d_isowner
d_ismember
d_findco
d_findfm
d_findlm
d_findnm
d_findpm
d_keyfrst
d_keylast
d_keyfind
d_keynext
d_keyprev
d_keyread
d_trbegin
d_trend
d_trabort
d_reclock
d_recfree
d_setlock
d_setfree
d_keylock
d_keyfree
d_freeall
8-26
Table 8-5. RDM Functions and Corresponding ida Command Sequences (continued)
Function
d_rlbset
d_rlbclr
d_timeout
d_setro
d_setrm
d_setor
d_setom
d_setoo
d_setmr
d_setmo
d_setmm
d_crget
d_crset
d_csmget
d_csmset
d_csoget
d_csoset
d_crstat
d_costat
d_cmstat
d_csstat
d_crtype
d_cmtype
d_cotype
8-27
8.2
Using wida
You can open more than one database in a wida window, and you may run more than
one wida window concurrently.
8.2.1.1
Many of the menu items call one or both of two common dialog boxes: the Records
dialog box and the Current Record dialog box. Note that when the Records dialog box
appears, the dialog box is not simply "Records". The name of the record type is included
as a prefix, and the scan type is included as a suffix. For example, if you are performing a
set scan on the AUTHOR record type, the title of the Records dialog box is this:
AUTHOR Records Set Scan
In this book, we will simply refer to this dialog box as the Records dialog box.
8-28
The Edit button displays a dialog box that allows you to modify a field value within
the record. You first must select the field to modify before clicking this button. The
dialog box for modifying field values has these buttons:
The First button displays in the dialog box the first field in the record.
The Last button displays in the dialog box the last field in the record.
The Next button displays in the dialog box the next field in the record. If the
dialog box currently shows the last field in the record, clicking the button causes
it to display the first field in the record.
The Prev button displays in the dialog box the previous field in the record. If the
dialog box currently shows the first field in the record, clicking the button causes
it to display the last field in the record.
The OK button approves the field changes in this record. It exits this dialog box
and returns you to the Current Record dialog box. To make the changes to this
record permanent, you still will need to use the Current Record dialog box to
store the record.
The Cancel button invalidates the field changes in this record and returns you to
the Current Record dialog box.
The Delete button deletes the current record. A dialog box appears to ask whether
you want to proceed before the program deletes the record.
The Store button writes the record to the database. If it is a new record, the record is
added; if it is an existing record, the record is updated.
8-29
The First button displays the first record in the current access method. If the dialog
box is displayed from a key operation, the First button displays the first record with
that key. If the dialog is reached by a set operation, the First button displays the first
member of the set. If the current record was obtained though a record scan, the First
button displays the first record of the current records type.
The Last button is the same as First button except that it displays the last record of a
requested operation.
The Next button displays the next record of a requested operation. If the current
record is the last record of that operation, the dialog box displays the first record of
that operation.
The Prev button displays the previous record of a requested operation. If the current
record is the first record of that operation, the dialog box displays the last record of
that operation.
The OK button clears the dialog box from the screen. If the record has been modified
or added without being stored first, a dialog box appears to ask whether you want to
store the record.
8-30
Open Database
This command opens a database for manipulation. It only opens a database if no other
databases are open. If other databases are currently open, it displays a dialog box that
asks whether you want the program to close those databases automatically. If you
decline, the program stops without letting you open a database. Otherwise, the program
displays the Open dialog box, in which you specify the database to open and the
conditions under which the database is opened.
Note: To open a database without having to close other open databases, use the
Incremental Open command.
The Open dialog box contains the following:
Lists of files and directories for selecting the database (.dbd file) to open
Option buttons for opening the database in Shared, Exclusive, or One User mode
A text box for entering the database user identifier (Dbuserid) for this task, which is
required if you are opening the database in Shared or Exclusive mode
Close Database
This command closes all open databases. To close a single database while leaving other
databases open, use the Incremental Close command.
Incremental Open
This command opens a database without closing other open databases. It displays the
same Open dialog box as the Open Database command. The database that you open
becomes the current database.
Incremental Close
This command closes a database without closing other open databases. It displays a
dialog box in which you select the database to close. After you close that database, the
program displays a message that shows what the current database is.
Database Number
This command allows you to denote another open database the current database. It
displays a dialog box in which you select the database to be current.
Initialize Database
This command initializes or re-initializes the data and key files in an open database. The
command displays a dialog box in which you select the database. After you make your
8-31
selection, you must confirm your action in a separate dialog box, because any stored data
in that database is deleted by the Initialize Database command.
Destroy Database
This command deletes the data and key files in an open database, but retains its database
dictionary file. The command displays a dialog box in which you select the database.
Quit
This command closes any open databases and then closes the wida window.
Scan
This command allows you to view or edit a record from a list of records that are sorted
on a key. Choosing this command displays a dialog box with a list of key names. Select
the desired key, and click OK. A list of records, which is sorted by that key, is displayed
in the Records dialog box. Select a record, and click OK. The record is then displayed in
the Current Record dialog box.
8-32
First
This command shows the record that contains the first value (in sorted order) of a
selected key. Choosing this command displays a dialog box with a list of key names.
Select a key, and click OK. The program then displays the record that is first when the
records are sorted by the selected key.
Last
This command shows the record that contains the last value (in sorted order) of a
selected key. Choosing this command displays a dialog box with a list of key names.
Select a key, and click OK. The program then displays the record that is last when the
records are sorted by the selected key.
Next
This command shows the record that contains the next value (in sorted order) of a
selected key. Choosing this command displays a dialog box with a list of key names.
Select the desired key, and click OK. The next record in key order is displayed in the
Current Record dialog box. However, if the current record for the key is already the last
record for that key, only a message box with this information appears.
Previous
This command shows the record that contains the previous value (in sorted order) of a
selected key. Choosing this command displays a dialog box with a list of key names.
Select the desired key, and click OK. The previous record in key order is displayed in the
Current Record dialog box. However, if the current record for the key is already the first
record for that key, only a message box with this information appears.
Delete
This command deletes an optional key field in the current record. Choosing this
command displays a list of keys for that record. To delete a key, select it from the list,
and click OK. If the selected key is not an optional key, a message box appears with that
information.
Exist
This command tests whether an optional key field in the current record has been stored.
Choosing this command displays a list of keys for that record. To test a key, select it
from the list, and click OK. If the selected key is not an optional key, a message box
appears with that information.
8-33
Store
This command stores an optional key field in the current record. Choosing this
command displays a list of keys for that record. To store a key, select it from the list, and
click OK. If the selected key is not an optional key, a message box appears with that
information.
Keybuild
This command rebuilds the key files in the current database. It corresponds to the
keybuild utility, which re-creates all key files in the current database by using the field
values that are stored in the data files.
Scan
This commands allows you to view or modify a member record in a set. Choosing this
command displays a dialog box with a list of set names. Select the set to scan, and click
the OK button. The set is scanned, and the records then display in the Records dialog
box. Select a record, and click OK. The record is displayed in the Current Record dialog
box.
8-34
First
This command shows the first member record in a set. Choosing this command displays
a dialog box with a list of set names. Select the set to scan, and click the OK button. The
first member of the set displays in the Current Record dialog box.
Last
This command shows the last member record in a set. Choosing this command displays
a dialog box with a list of set names. Select the set to scan, and click the OK button. The
last member of the set displays in the Current Record dialog box.
Next
This command shows the next member record in a set. Choosing this command displays
a dialog box with a list of set names. Select the set to scan, and click the OK button. The
next member of the set displays in the Current Record dialog box. However, if the
current set member is already the last set member, the program simply displays a dialog
box with this message.
Previous
This command shows the previous member record in a set. Choosing this command
displays a dialog box with a list of set names. Select the set to scan, and click the OK
button. The previous member of the set displays in the Current Record dialog box.
However, if the current set member is already the first set member, the program simply
displays a dialog box with this message.
Owner
This command finds the current owner of the current record in a set. Choosing this
command displays a dialog box with a list of set names. Select the set to scan, and click
the OK button. The sets owner record (stored as the current record) then displays in the
Current Record dialog box.
Connect
This command connects the current record to the current owner of a set. Choosing this
command displays a dialog box with a list of set names. Select the set to scan, and click
the OK button. The current record is connected to the set. If it is not a valid member of
the set, a message box appears with that information.
Disconnect
This command disconnects the current record from a set. Choosing this command
displays a dialog box with a list of set names. Select the set, and click OK. After
8-35
selection, the current record is disconnected from the set. If the current record is not
connected to the set, then a message box appears with that information.
Members
This command displays the number of members that are in a set. Choosing this
command displays a dialog box with a list of set names. Select the set to scan, and click
the OK button. The total number of members in the set are displayed. This is obtained
with a d_members call.
Is Owner?
This command tests whether the current record is the owner of member records.
Choosing this command displays a dialog box with a list of set names. Select the set to
scan and click the OK button. A d_isowner function call is made to determine if the
current record has member records attached to it. A message box displays with the result
of the function call. If the current record is not of the same record type as the record type
of the set owner, a message box appears with this information.
Scan
This command is used to select a record for viewing. Choosing this command displays a
list of record types. Select the record type that you want to see, and click the OK button.
8-36
The records of that record type are displayed in the Records dialog box. Select a record
to display, and click OK. That record becomes the current record, and field values for
that record are shown in the Current Record dialog box.
First
This command is used to view the first record of a selected record type. Choosing this
command displays a list of record types. Select the desired record type, and click OK.
The first record of that record type becomes the current record, and it is displayed in the
Current Record dialog box.
Last
This command is used to view the last record of a selected record type. Choosing this
command displays a list of record types. Select the desired record type, and click OK.
The last record of that record type becomes the current record, and it is displayed in the
Current Record dialog box.
Next
This command is used to view the next record of a selected record type. Choosing this
command displays a list of record types. Select the desired record type, and click OK.
The next record of that record type becomes the current record, and it is displayed in the
Current Record dialog box. However, if the current record of that record type is its last
record, only a message box with this information appears.
Previous
This command is used to view the previous record of a selected record type. Choosing
this command displays a list of record types. Select the desired record type, and click
OK. The previous record of that record type becomes the current record, and it is
displayed in the Current Record dialog box. However, if the current record of that
record type is its first record, only a message box with this information appears.
Modify
This command modifies the current record. Choosing this command displays the current
record in the Current Record dialog box. For each field value that you want to change,
8-37
select the field, and click Edit to display the dialog box in which you can specify another
value. After making the desired changes, click the Store button in the Current Record
dialog box to write the record to the database.
Delete
This command deletes the current record from the database. Choosing this command
requires you to confirm the action before the program actually deletes the record. If the
current record is currently the owner or a member of any sets, the deletion does not
occur, and an error message appears. To delete the record, you must use the Disconnect
& Delete command, which automatically disconnects the current record from any sets
before deleting the record.
8-38
8-39
Begin
Choosing this command displays a dialog box with an edit box to name the transaction.
It calls d_trbegin to start a transaction.
End
Choosing this command calls d_trend to end a transaction.
Abort
Choosing this command calls d_trabort to abort a transaction.
8-40
Lock Types
Choosing Lock Types from the Locks menu displays the Lock Record, Set or Key Type
dialog box. In this dialog box, a list box initially shows the record types in the current
database and indicates whether the lock status is f for free, r for read lock, w for write
lock, x for exclusive lock, or k for keep lock.
The option buttons in the Lock section of the dialog box control whether the list box
displays record locks, set locks, or key locks. To display a different type, click the option
button you want.
Free Types
Choosing Free Types from the Locks menu displays the Free Record, Set or Key Type
dialog box, shown below. In this dialog box, a list box shows the record types in the
current database and indicates whether the lock status is f for free, r for read lock, w for
write lock, x for exclusive lock, or k for keep lock.
As with the option above, this command displays a list box with the current lock values.
Radio buttons control whether the list displays record locks, key locks, or set locks.
Select the type of object and the object, and then free the lock by pressing the Free button.
You can also select the Free All button to free all locks on all objects.
8-41
Cache Pages
Choosing this command displays a dialog box with an edit box for the cache pages and
one for the overflow index pages to use. This can only be done before a database is
opened.
File Handles
Choosing this command displays an edit box to set the maximum number of data and
key files that RDM may have open for this task.
8-42
Close Handles
Choosing this command closes all files that RDM is currently holding open.
Lock Manager
Choosing this command brings up a dialog box that allows you to select a
communication type (protocol) and name for communicating with the lock manager.
This command can be performed only if all databases are closed.
8-43
Closefiles
(CLOSEFILES) When this option is on, RDM closes files automatically after all input and
output is completed. Otherwise, RDM leaves those files open. The default value for this
option is off.
Delete Chain
(DCHAINUSE) When this option is on, RDM allocates any new records from the delete
chain before any new pages are added. Otherwise, RDM allocates new records to the
end of the file. The default value for this option is on.
Delete Log
(DELETELOG) When this option is on, RDM deletes the database log file automatically
when the database is closed. Otherwise, the log file is kept. The default value for this
option is off.
Ignorecase
(IGNORECASE) When this option is on, RDM sorts strings without regard to case.
Otherwise, the sorting of strings is case-sensitive. The default value for this option is off.
Prealloc
(PREALLOC_CACHE) When this option is on, RDM allocates the entire cache when a
database is opened. The default value for this option is off. This option can be changed
only if all databases are closed.
Readonly
(READONLY) When this option is on, any databases that you open can be read but not
modified. Otherwise, you have full access to the opened databases. (Note that even if
this option is off, you can choose to open individual databases in read-only mode.) The
default value for this option is off. This option can be changed only if all databases are
closed.
Remotesync
(REMOTESYNC) When this option is on, RDM locks a file automatically whenever it is
accessed. The default value for this option is off.
Syncfiles
(SYNCFILES) When this option is on, RDM forces file commits whenever a transaction is
ended, so that the files remain synchronized. Otherwise, the transaction remains in the
system cache. The default value for this option is on. This option can be changed only if
all databases are closed.
8-44
Transaction Logging
(TRLOGGING) When this option is on, RDM automatically logs all transactions.
Otherwise, no transaction logging is done. The default value for this option is on. This
option can be changed only if all databases are closed.
TxTest
(TXTEST) When this option is on, RDM can test how the recovery of transactions is
working. Otherwise, RDM is unable to test transaction recovery. The default value for
this option is off.
RDM Function
File Menu
Open Database
d_open
Close Database
d_close
Incremental Open
d_iopen
Incremental Close
d_iclose
Database Number
d_setdb
Initialize Database
d_initialize
Destroy Database
d_destroy
Exit
N/A
Key Menu
Scan
d_keyfrst, d_keynext
First
d_keyfrst
Last
d_keylast
Next
d_keynext
8-45
RDM Function
Previous
d_keyprev
Delete
d_keydel
Exist
d_keyexist
Store
d_keystore
Keybuild
d_keybuild
Scan
Set Menu
d_findfm, d_findnm
First
d_findfm
Last
d_findlm
Next
d_findnm
Previous
d_findpm
Owner
d_findco
Connect
d_connect
Disconnect
d_discon
Members
d_members
Is Owner?
d_isowner
Scan
Record Menu
d_recfrst, d_recnext
First
d_recfrst
Last
d_reclast
Next
d_recnext
Previous
d_recprev
d_fillnew
Modify
d_recwrite
Delete
d_delete
d_disdel
Currency Menu
d_setom, d_setoo, d_setor, d_csoset
8-46
RDM Function
Transactions Menu
Begin
d_trbegin
End
d_trend
Abort
d_trabort
Locks Menu
Lock Types
Free Types
d_rlbset
d_rlbclr
d_rlbtst
d_internals
Special Menu
Cache Pages
d_setpages
File Handles
d_setfiles
Close Handles
d_closeall
d_dblog, d_dbtaf
Lock Manager
d_lockcomm
Options Menu
Closefiles
d_on_opt, d_off_opt
Delete Chain
d_on_opt, d_off_opt
Delete Log
d_on_opt, d_off_opt
Ignorecase
d_on_opt, d_off_opt
Prealloc
d_on_opt, d_off_opt
Readonly
d_on_opt, d_off_opt
Remotesync
d_on_opt, d_off_opt
Syncfiles
d_on_opt, d_off_opt
Transaction Logging
d_on_opt, d_off_opt
TxTest
d_on_opt, d_off_opt
About wida
Help Menu
N/A
8-47
Chapter 9
RDM Unicode Support
9.1
Support for Unicode varies widely for different operating systems; consequently, these
differences affect the levels of support available in RDM. Currently Unicode support is
very complete for Windows NT, but support in UNIX is much more limited.
On Win32 all library functions exist both as ANSI and as Unicode versions; for example,
two versions exist for all I/O and GUI functions. This means that as an RDM user, you
can write applications that use only Unicode strings, and may therefore want to specify
paths, user IDs, etc., in Unicode. Thus for Win32, we need an RDM library in which all
string arguments are Unicode strings.
Note: "Win32" refers to Windows NT not Windows 95 or 98, which do not support
Unicode.
On UNIX, Unicode string manipulation functions are provided, but I/O functions are
not. Some display functions, such as puts() and gets(), have Unicode equivalents, but not
all. This level of support makes it unlikely that as an RDM user, you will write
applications that use only Unicode strings, or that you will require an RDM library with
Unicode string arguments.
The most common standard for storing international character sets on UNIX is UTF-8
(see note below). This is an encoding standard that provides a method for mapping
Unicode characters to multi-byte sequences, so that they can be stored as conventional
8-bit character strings. RDM 5.0 supports UTF-8 on platforms where the standard is
available.
Note: The acronym UCS stands for "Universal Character Set". The set was developed
jointly by the Unicode Consortium and the International Organization for
Standardization (ISO). UTF is an acronym for "UCS Transformation Format".
9-1
been able to store. However, if these fields are defined as keys, RDM may not sort them
as required. Earlier versions of RDM have sorted strings by their binary values, but
Unicode strings should be sorted according to a sequence which can be more complex.
String collation functions, such as _wcsnicoll in Win 32, are provided in the OS for doing
locale-specific collation of Unicode strings. Thus RDM must support Unicode string
fields as a separate type from their underlying data types. Apart from their size, Unicode
string fields should be treated in much the same way as regular, char string fields.
Definitions
The data type used for Unicode characters is called wchar_t on all the systems supported
by RDM. (Most of these systems also define _WCHAR_T, which is the equivalent of
wchar_t.) Unfortunately, the definition of wchar_t is not the same across all platforms:
Table 9-1. OS Definitions for wchar_t
Platform
wchar_t Definition
Win32
unsigned short
Solaris
long
Unixware
long
HPUX
unsigned int
All of the platforms in the table above provide Unicode equivalents of string handling
functions such as strcpy(), strcmp(), etc. Typically, these functions are named wcscpy(),
wcscmp(), etc. In addition, Win32 provides macros and typedefs (in tchar.h), which
allow the user to write code that can be compiled for Unicode or ANSI string handling;
UNIX does not provide these. Win32 programs (such as RDM) that use these macros
must define the symbol _UNICODE at compile time if Unicode is required.
9.2
2.
Unicode library functions, with Unicode string arguments and full internal string
handling in Unicode, supported on Win32 only.
To enable the first level of support, the preprocessor symbol UNICODE_DATA must be
defined when RDM is compiled. This symbol is defined automatically in vista.h, on all
platforms.
To enable the second level of support, the preprocessor symbol UNICODE must be
defined when RDM is compiled. This symbol is not defined by default on any platform,
and should only be defined for Win32. If it is defined on any other platform a compiler
error will result. The batch files and makefiles supplied with RDM for Win32 will define
the preprocessor symbol UNICODE if an environment variable of the same name is set.
Thus before running the batch files, type the following:
set UNICODE=1
9-3
rdm50.dll
Unicode DLL:
rdm50u.dll
The functionality of the two DLLs should be identical, except for Unicode support. There
is no advantage in using the Unicode DLL in an application that does not manipulate
foreign language data.
RDM 5.0 for Win32 includes two complete sets of binaries (all DLLs, LIBs and programs),
a Unicode set, and an ANSI set. The functionality of the utility programs is the same
and, except for the Lock Manager, they are interchangeable. For example, it makes no
difference whether a Unicode or an ANSI version of dbcheck.exe is run; the output
should be identical.
The Unicode and ANSI Lock Manager programs are not interchangeable, because of
differences in message formats (see below). This means that applications that use the
Unicode DLL (rdm50u.dll) must also use the Unicode Lock Manager (lmwu.exe).
Applications that use the ANSI DLL (rdm50.dll) must use the ANSI Lock Manager
(lmw.exe).
To make the Unicode programs distinguishable from the ANSI ones, their filenames
include the letter "u", as shown below. Also, the word "Unicode" appears in the version
string they display when they are run.
9-4
adstatus.exe
N/A
dal.exe
dalu.exe
datdump.exe
datdumpu.exe
dbcheck.exe
dbchecku.exe
dbclrlb.exe
dbclrlbu.exe
dbedit.exe
dbeditu.exe
dbexp.exe
dbexpu.exe
dbimp.exe
dbimpu.exe
dbrev.exe
dbrevu.exe
dchain.exe
dchainu.exe
ddlp.exe
ddlpu.exe
initdb.exe
initdbu.exe
keybuild.exe
keybldu.exe
keydump.exe
keydumpu.exe
keypack.exe
keypacku.exe
lmclear.exe
lmclearu.exe
lmw.exe
lmwu.exe
prdbd.exe
prdbdu.exe
w_query.exe
w_queryu.exe
wconsole.exe
wconsolu.exe
wida.exe
widau.exe
9-5
Function Prototypes
In general, RDM functions that take (char *) string arguments in non-Unicode versions of
RDM take (wchar_t *) string arguments in the Win32 Unicode DLL. A Win32 application
that calls RDM functions such as dt_dbuserid or dt_dbdpath must supply (wchar_t *)
arguments if it uses the Unicode version of the RDM library.
If the functions in the ANSI and Unicode versions of the RDM DLL had the same names,
there would be a risk of application developers accidentally using the wrong library.
This mistake would probably not be detected until the program crashed, and the cause of
the crash might not be obvious. Therefore, a function naming strategy has been adopted
in RDM similar to that used by Microsoft in its Windows DLLs. Unicode function names
bear the suffix "W", while ANSI function names bear the suffix "A". See the following
example.
ANSI prototypes:
int dt_dbuseridA(const char *, DB_TASK *);
int dt_dbdpathA(const char *, DB_TASK *);
Unicode prototypes:
int dt_dbuseridW(const wchar_t *, DB_TASK *);
int dt_dbdpathW(const wchar_t *, DB_TASK *);
9-6
The application developer can use the same function names as in previous versions of
RDM, for example, dt_dbuserid and dt_dbdpath. Macro definitions are provided in the
RDM include files that map these names to the actual function names, dependant on
whether UNICODE is defined at compile time. See below:
#ifdef UNICODE
#define dt_dbuserid dt_dbuseridW
#else
#define dt_dbuserid dt_dbuseridA
#endif
#ifdef UNICODE
#define dt_dbdpath dt_dbdpathW
#else
#define dt_dbdpath dt_dbdpathA
#endif
All other RDM function prototypes follow this same pattern. Thus the application
developer is prevented from passing the wrong string type to an RDM function, (by
compiler errors), and prevented from linking to the wrong library (by linker errors).
Note that the database open mode in dt_open, and the lock status characters in
d_reclock, d_setlock, etc., are defined as char in all versions of RDM. These arguments
have not been redefined as wchar_t in the Unicode implementation, as they can only ever
take certain pre-defined values ("o", "s" or "x" for the open-mode), which can always be
represented in char variables.
On UNIX, where no Unicode library is provided, the function prototypes are the same as
for RDM 4.5.
9.3
9-7
9-8
Used by all versions of RDM from 3.0 to 4.5, with ANSI filenames up to 48
characters long
v3.01
v5.00
u5.00
These DBD formats are supported by RDM versions as shown in the table below:
v3.00
v3.01
v5.00
RDM 4.9.3
RDM 5.0
Unicode
u5.00
Non-Unicode Interfaces
The network protocols used for Lock Manager communication on Win32 generally do
not support Unicode. This means that, in the Unicode implementation of RDM, the Lock
Manager name has to be converted to ANSI before it is passed to the network protocol.
The same restriction may also apply to user IDs. Therefore it is a good practice to use
names which can be converted to ANSI.
9-9
9.4
Besides the DBD file and Lock Manager communication (see above), the following data
structures can be shared by RDM applications on a network. These structures contain
character strings which, in Unicode versions, will be wchar_t strings:
lmc_type;
user_count;
lmc_lockmgrn[48];
cnt;
files[9][48];
All these data structures are incompatible between Unicode and ANSI versions of RDM.
9-10
The TAF file includes a flag (in the upper half-byte of the lmc_type element), which is set
if the TAF file was generated by a Unicode RDM library. This enables RDM 5.0, as well
as prior versions, to reject incompatible TAF files. Thus, when differences exist in the
position of the cnt element, you avoid the risk of triggering an unnecessary
auto-recovery.
The version number stored in the DBL file (DBL_VER) is also different for Unicode and
non-Unicode implementations, again allowing all versions of RDM to reject incompatible
DBL files.
9.5
The prototypes for approximately one third of the RDM library functions are different in
the Unicode version than in the standard version. These are all functions containing
char* data types. In the majority of these, char* becomes wchar_t* and these are shown
in List One below. Several functions that take the char* data type, however, do not
change in the Unicode version. These are found in List Two below.
Note: Unicode and non-Unicode function prototypes may not be mixed. For those
functions that change for Unicode, only char* is available in an ASCII RDM runtime, and
only wchar_t* is available in a Unicode RDM runtime.
List One
See the list below for functions taking the char data type, which have a separate
prototype for Unicode (char changes to wchar_t):
d_checkid
d_dbtmp
d_open
d_ctbpath
d_dbuserid
d_rdmini
d_dbdpath
d_destroy
d_rdmve
d_dbfpath
d_iopen
d_recover
d_dblog
d_lmclear
d_renfile
d_dbnum
d_lmstat
d_trbegin
d_dbtaf
d_lockmgr
9-11
List Two
The functions listed below take the char data type, but do not have a separate prototype
for Unicode (char remains char):
d_keylock
d_reclstat
d_keylstat
d_setlock
d_mapchar
d_setlstat
d_open
d_trlog
d_reclock
Note that one function, d_open, appears on both lists. Its prototype is shown below:
int EXTERNAL_FIXED d_open(P1(CONST DB_TCHAR DB_FAR *)
Pi(CONST char DB_FAR *));
9-12
Chapter 10
File Transfer Utilities (dbimp, dbexp)
10.1 Introduction
RDM stores all its data in a special-purpose format. This format is specific to RDM, and
all data in an RDM database must have been created by the RDM runtime functions. In
the development of a system, data often already exists in a format other than that used by
RDM, the most common being ASCII format. RDM cannot operate directly on ASCII text
files. This chapter provides information about RDMs dbimp and dbexp utilities, which
transfer data from an ASCII text file into RDM. For information about the way that RDM
handles Unicode text files, see Chapter 9.
It is possible to write RDM applications to read data from a text file and enter it into
RDM records, or vice versa. Entering bulk data from an external source into a database is
called importing. Reading data from RDM and writing it in a different format is called
exporting. To accomplish these tasks, two general-purpose utilities are provided as a
part of the RDM tool set. The utility that imports data to an RDM database is called
dbimp. The utility that exports data is called dbexp.
These utilities use ASCII text files as the standard external representation of data. These
files can be created or modified by text editors or string manipulation programs. Most
database management systems have the ability to export data into ASCII (see Figure
10-1).
Both dbexp and dbimp view an ASCII record as one line of text, terminated by a newline
character (and Enter character on some computers), with fields separated by a standard
separator character. Our utilities use the comma as a separator character. . Numeric
fields are represented by ASCII digits, in base 10. Character fields are represented by
ASCII alphanumeric characters, optionally enclosed by double quotation marks. The
fields (or records) are not fixed-length.
10-1
dbexp
dbimp
ASCII
Text Files
RDM
Database
Foreign
DBMS
RDM
Database
Text
Editor
10-2
A second record type shown below would be stored in a different text file:
0:2,"4GL"
0:3,"languages"
0:4,"reliability"
0:5,"debugging"
0:6,"testing"
0:7,"database design"
This would export data from the info record type of the tims database. The info record
type looks like the following:
record info {
unique key char id_code[16];
char info_title[80];
char publisher[32];
char pub_date[12];
int info_type;
}
The file created by dbexp would be named info.txt and would contain records created
from the contents of the corresponding RDM data file, as follows:
"db001","Database Design","McGraw Hill","1983",0
"db002","An Intro...","Addison Wesley","1981",0
"db005","Fundamental ...","Yourdan","1981",0
10-3
The -s option indicates that the following argument is a character, enclosed in quotes, to
be used as a separator character. The default is a comma. For example, if a vertical bar
character were desired, the following command could be used:
dbexp -s "|" tims info
The -e option changes the escape character (default is backslash), which is to be used in
front of each quotation mark character contained within a character string, or each
backslash character contained in a string. For example, if a field contains the following
string:
Your "filing cabinet" is called C:\DRAWERS.
dbexp would convert the string into the following format as it is written into the ASCII
file:
"Your \"filing cabinet\" is called C:\\DRAWERS."
You may wish to change the escape character if text in your database contains many
backslashes. This would prevent each backslash from becoming a double backslash.
Note, however, that if the exported data will be used as input to dbimp, the same escape
character must be specified in the dbimp command.
The -r option causes dbexp to include the records database address (see section 14.2.2,
"Data File Organization") as the first field in each ASCII record. For example:
dbexp -r tims info
Since the database address of each record is unique, this field can be used as a unique
identifier of each record. The import utility can take advantage of this field.
The -m option causes set membership information to be included with each record. This
information consists of a list of database addresses, which are placed after the records
own database address (if -r is specified) and before the ASCII data fields. One database
address field is created for each set for which this record type is a member. The value of
each field is the database address of the owner record for that set. If the record is not
connected to the set, the field value is zero.
10-4
The -n option is a silent option; there is no output to stdout. The -d option causes a
database address to be printed as long integers, rather than in the standard format. Note
that a database address may appear as a very large number (the maximum is
4,294,967,295) because of the internal format of a database address.
The order of the database address fields is the same as the order of the sets in the DDL. If
a record is a member of three sets, three database addresses would be included in each
ASCII record. The first database address would represent the owner of the first set in the
schema that lists this record type as a member. This ordering information is important
when using the import utility to recreate the set connections, because it can be used to
create a logical connection between fields with identical values.
The command below:
dbexp -r -m tims info
The info record type is a member of two sets. This data has been connected in the first
set (has_published), but not in the second (articles), as indicated by the NULL in the
third field.
10-5
dbimp
Import
Specification
ASCII Text
ASCII Text
RDM
Database
10-6
can specify up to 228 characters. Note that the larger the number, the larger the
temporary key file needs to be. To decrease the size of the CRI keys, use a value of n less
than 25.
The -pn option makes dbimp call d_setpages with n as the number for the database
pages. By default, n is 17. To increase performance, increase n. To save on memory, n
may be set as low as five.
The import specification language is contained in the file impspec. This is a text file
containing the specification that has been written by the dbimp user. It may have any
legal file name, but we recommend the following naming convention:
dbname.imp
The suffix of .imp will distinguish this file as an import specification for the database
dbname.
The goal is to import the ASCII records into the RDM records. The first ASCII field is a
last name, the second is a first and middle name, and the third is an encoded birth date
(format YYY MM DD, where 1000 must be added to the year). The fields in the RDM
10-7
record happen to be in the same order. Our import specification will be stored in file
tree.imp, and will contain the following statements:
database tree;
foreach "person.asc" {
record person {
field p_last = 1;
field p_first = 2;
field p_birth = 3;
}
}
end;
Note that there are two phases in the execution of the import utility. The first phase is
the compilation phase, where the import specification is read and compiled. If there are
any errors or warnings in the specification, messages will be printed before the
"Compilation complete" message, and the import will be terminated. The specification
must compile correctly before the utility will open and update the database. This second
phase is the import phase. Its activity is logged between the "Starting data import" and
"Successful import" messages. Normal output is a copy of the input records. If there are
any problems during the import, error and warning messages will appear between the
input records. Often, the warnings can be ignored, but all should be examined for
potential problems in the input data.
This first example has illustrated the use of five import specification statements:
database, foreach, record, field, and end.
The database statement identifies the database to be opened and updated. Records may
exist in a database if they will not collide with unique keys that are being imported.
Existing records will not be altered by the import, nor can they be accessed; the new data
will be completely disjoint from existing data.
10-8
The foreach statement specifies an ASCII text file (within quotation marks), followed by
a block of statements enclosed in braces. It may be interpreted as saying, "for each line in
this file, perform the following operations." During the import phase of the utility, it will
repeatedly read one line from the named file and process its contents according to the
enclosed statements. When the last line of text has been read, the statement following the
closing brace will be executed. In this simple example, the next statement is an end
statement.
The record statement names an RDM record type, followed by a block of statements
enclosed in braces. Each time the record statement is executed, a record of the named
type is conditionally created. There is a way to skip the creation of a record if a record
with the same contents has already been created. This capability will be discussed below.
In this example, a record will be created for each input line of text.
A field statement defines a mapping between an RDM record field (named left of the
equals sign), and an ASCII field (identified by its numeric position in the record). The
input field is converted into the type of the RDM field. In this example, the first and
second fields in the input record are converted into strings (by adding a null terminator
following the last character), and placed into the p_last and p_first fields. Then the third
input field is converted into a long integer, and placed into the p_birth field. The record
is actually created with the d_fillnew function, so that all keys are automatically created
along with the record.
The field statement implements the mapping between the ASCII and RDM records. For
each record statement, zero or more field statements may be used, not to exceed the
number of fields defined in the record. Not all fields in the RDM record require a field
statement. Unspecified fields are zero-filled. Likewise, it is not necessary to use all
ASCII fields in a field statement. The ordering of the fields is irrelevant.
If dbimp is unable to fully convert the input data into the RDM field type, it will print a
warning message, do the best it can, and go on. All other fields are filled. For example,
the length of the text in an input field may be longer than the space allowed for it in the
record. The utility will fill the RDM field to its maximum length, and ignore any
remaining characters.
10-9
Your schema will not contain redundant data but will instead utilize a set construct:
record author {
key char name[32];
}
record book {
char title[52];
long pub_date;
}
set published {
order ascending;
owner author;
member book by pub_date;
}
In this example, each line of text contains parts of two record types. This implies that
two record statements should be included within the one foreach statement. The
resulting database should contain four book records, but only two author records. The
first author record should be connected, via the published set, to three book records,
while the second author record should be connected to only one. The following import
specification will do just that:
database books;
foreach "books.asc" {
record author {
create on 1;
field name = 1;
}
record book {
field title = 3;
field pub_date = 2;
}
connect published;
}
end;
In this example, two records are conditionally created for each ASCII text line. The
create on statement is used to make sure that a new author record is created only if an
identical record has not already been created. Another statement, connect, is used to
perform a d_connect function between the two records created in the loop.
The create on statement causes dbimp to search its internal created record index (CRI)
for the existence of a record with the same record type and field value. The field value
stored in the CRI may or may not be stored in the record itself. In this example, the field
used in the create on statement is also used in the record. Other import specifications
may use the field values only to establish connections, and not for storage in the RDM
record.
If the create on statement searches the CRI for a matching record type and field value
and does not find it, it will create the record and an entry in the CRI, storing the database
address of the created record in the CRI entry. If it finds a match, it does not create a new
10-10
record, but uses the database address stored in the entry to represent the record that
would have been created.
The import utility maintains another internal list, called current of record type (CRT).
This is a currency table similar to the current set, owner, and member tables maintained
by the RDM runtime functions. Whenever a record is created, the database address of
that record is stored as the current record of its type. The connect statement will search
the schema tables for the correct owner and member types, then extract the database
addresses of those types from the CRT table. If there are no current records of both
types, dbimp will not attempt a connection, and will skip to the next iteration of the
foreach loop. Because only one record of each type is being tracked, dbimp cannot
connect recursive sets; that would require two records of that one type to be tracked.
We will perform the above example, showing the CRI and CRT list at key points. The
statement to be executed is shown, followed by a description of the actions performed by
dbimp in processing the statement.
Statement:
foreach "books.asc" {
Statement:
record author {
create on 1;
Save CRT:
author:
book:
[0:1]
NULL_DBA
Statement:
field name = 1;
10-11
Statement:
record book {
field title = 3;
field pub_date = 2;
}
Unconditionally create book record ([1:1]). No entry created in CRI. Save CRT:
author:
book:
[0:1]
[1:1]
Determine that author is owner, book is member. Make current book, [1:1], the current
record. Make current author, [0:1], the current owner of set published. Call
d_connect(PUBLISHED).
Statement:
foreach "books.asc" {
Statement:
record author {
create on 1;
field name = 1;
}
Search CRI for match (no match). Create author record (database address [0:2]), and add
it to the CRI:
key={author,"Knuth, D."}, data=[0:1]
key={author,"Ullman, J."}, data=[0:2]
Update CRT:
author:
book:
[0:2]
[1:1]
10-12
Statement:
record book {
field title = 3;
field pub_date = 2;
}
Unconditionally create book record ([1:2]). No entry is created in the CRI. Save CRT:
author:
book:
[0:2]
[1:2]
Set field title to "Principles of Database Systems". Set field pub_date to 1982.
Statement:
connect published;
Determine that author is owner, book is member. Make current book, [1:2], the current
record. Make current author, [0:2], the current owner of set published. Call
d_connect(PUBLISHED).
Statement:
foreach "books.asc" {
Statement:
record author {
create on 1;
Search CRI for match on: {author,"Knuth, D."}. Match is found; set CRT:
author:
book:
[0:1]
[1:1]
Statement:
field name = 1;
Statement skipped.
Statement:
record book {
field title = 3;
field pub_date = 2;
}
10-13
Unconditionally create book record ([1:3]). No entry is created in the CRI. Save CRT:
author:
book:
[0:1]
[1:3]
Determine that author is owner, book is member. Make current book, [1:3], the current
record. Make current author, [0:1], the current owner of set published. Call
d_connect(PUBLISHED).
The last iteration of the foreach loop will have the same effect as the third, where a third
book record is connected to the first author record.
Person
Husband
Wife
Offspring
Marriage
Fig. 10-3. Family Tree Schema
10-14
The input data contains field-value relationships that will be converted into set
relationships during the import. There are two data files: one for definitions of people in
the tree and a second for the definitions of marriages in the tree. One person may be
either a husband or wife in a marriage, and a person may be the offspring of a marriage.
The definitions of the person fields in file person.asc are as follows:
1.
Numerical reference to the marriage from which this person is the offspring.
2.
3.
4.
5.
6.
10-15
1.
2.
3.
4.
Marriage date.
5.
File: marriage.asc
2,4,21,9731215,0
5,9,19,9511013,0
10,11,13,9260313,9270561
20,15,17,9290324,0
30,23,7,9841013,0
31,8,24,9840512,0
32,9,25,9740000,0
33,9,26,9820000,0
As these data are imported, we want to be able to connect each person record to the
marriage record(s) it is related to in two ways. A person may be a spouse in one
marriage, and may be the offspring of another marriage. In each line of ASCII person
data is a unique identifier of the person, plus the identifier of the marriage of which this
person is the offspring. Hence it is possible to create up to two records for each line of
person data. As the person and marriage records are created or found, the offspring set
can be connected.
The loop through person.asc will cause all person and marriage records to be created,
and all offspring connections to be made. A loop through marriage.asc will allow each
10-16
marriage record to be found and updated with full information about the marriage. It
will also allow the husband and wife set connections to be made.
The following import specification demonstrates the update on and find on statements,
as it updates marriage information and creates husband and wife connections, both in the
second foreach loop.
database tree;
foreach "person.asc" {
record person {
create on 2;
field p_num = 2;
field p_last = 3;
field p_first = 4;
field p_birth = 5;
field p_death = 6;
}
record marriage {
create on 1;
}
connect offspring;
}
foreach "marriage.asc" {
record marriage {
update on 1;
field m_date = 4;
field m_term = 5;
}
record person {
find on 2;
}
connect husband;
record person {
find on 3;
}
connect wife;
}
end;
10-17
Read each line of ASCII text from input_file and make the text available to the import
statements enclosed in the loop. When an end-of-file is detected in input_file, exit the
loop and execute the next statement.
Import Statement
foreach_loop | record_statement | connect_statement
Note that a foreach_loop can contain a foreach_loop. This will cause an inner input_file
to be read and processed repetitively. Any number of import_statements may be
included within one foreach_loop.
Record Statement
record recname {
[ handling ]
[ field_statement ]
...
}
Depending on the handling, create record type recname, read it to be updated, or find a
previously created record instance. If field_statements are present, convert the ASCII
data into the proper types, and copy the values into the record.
10-18
Handling
create on field_number ;
or
update on field_number ;
or
find on field_number ;
If the handling is create on, search the created record index (CRI) for a record of the same
type and field value. If found, make the existing record the current of record type (CRT)
and do not create a new record and ignore any field_statements. If not found, create a
new record, enter its type and field value into the CRI, make it the new CRT, and
perform the following field_statements, if present.
If update on, search the CRI for a record of the same type and field value. If such a
record is found, read in the existing contents of the record and update them from the
following field_statements. Otherwise, create a new record and enter it into the CRI.
Make the record the CRT.
If find on, search the CRI for a record of the same type and field value. If found, make it
the CRT. Otherwise, nullify the CRT for this record type. Ignore any field_statements
that follow.
Field Statement
field field_name = [input_file.]field_number ;
Convert the ASCII data in field field_number into the type defined in the schema for field
field_name, and copy the value into the record being created.
If more than one foreach_loop level exists, data can be used from any active line of ASCII
text. The default text comes from the inner-most loop. To refer to other active lines of
text, precede the field number with the input_file. For example:
foreach "invoice.asc" {
...
foreach "item.asc" {
record item {
...
field inv_no = "invoice.asc".1;
field item_no = 1;
...
The field_name may be composed of a structure name and an element name, if the field is
in a structured field. The structure name and element name may be subscripted, if they
are defined as arrays.
If a partial specification is given (for example the structure name only, with no element),
the whole structure is implied. The field_number points to the first field that will be used,
10-19
with the others immediately following. For example, a record type may be defined with
the following field:
struct {
char street[20];
char city[20];
long zip
} address;
Arrays are handled similarly. If a complete reference is given, only one field value is
used. If a partial reference is given, the field numbers following the field_number in the
statement are utilized to satisfy the assignment. Consider the following field definition:
struct {
int code;
long message[10];
int marker;
} packet[3];
Character strings are treated differently than other types of arrays. The last dimension in
a character field definition represents the length of a string, which is read from just one
ASCII field. If a character field definition contains more than one dimension, it is
assumed to be an array (or matrix) of character strings. The only exception to this rule is
when the last dimension is one. This defines the field as binary data, which is to be
interpreted as hexadecimal data in the input text (see section 10.3.3, "Data Conversion").
An example multi-dimensional character string field follows:
10-20
char memo[20][80];
A field statement using only the name memo, with no subscripts, would imply that 20
fields would be read from an ASCII record, each containing strings up to 80 characters
long. If the statement used one subscript, then one text field would be read into the RDM
field. Using two subscripts with the name would be invalid.
Connect Statement
connect setname ;
Look up the owner and member types in the database dictionary. If the CRT table
contains database addresses of these types, make the owner type the current owner of set
setname, and make the member type the current record. Then perform a d_connect
function with set setname. If owner and member do not exist as the CRT, then do not
perform the connect. If the current record is already connected to the set, ignore the
connect.
10-21
necessary, and assigned to the actual target data type. If the target type is float or
double, the format will be %lf, which will convert the string into a double type variable.
It is then assigned to the actual target data type.
Note: Single-byte character fields can be imported as their ASCII value in decimal or
octal or as the character. The standard C escaped characters are also supported.
Binary data is defined when the last dimension of a character field is 1. See the example
below.
char picture[256][1];
Since a standard C character string cannot be represented by one character, RDM will
interpret this type of definition as binary data without null terminators. The input string
expected by dbimp will consist of a string of hexadecimal values, two characters per
hexadecimal byte value, with no blanks between them. For example, to read the string
"Hello world" (including the null terminator) into a binary field 12 characters long, the
input field should be:
48656c6c6f20776f726c6400
The export utility, dbexp, will create this representation from any binary fields.
10-22
ordering of any of these types of sets. Both ascending and descending set types will be
transferred, because they are sorted during the d_connect call performed by dbimp.
Alternatively, you might consider using the ASCII file output capability of db_QUERY
for exporting your data. db_QUERY can maintain the original set ordering.
The second restriction exists because dbimp has no mechanism to perform connections
where both owner and member are the same type of record (recursive sets). Note that
these types of sets may exist in the database; they just cannot be re-connected during an
import.
10-23
Chapter 11
Database Access Language (dal)
11.1 Introduction
The Database Access Language (dal) is intended as a tool for interactive or batch
manipulation of an RDM database.
dal allows you to execute a subset of the RDM runtime functions, either interactively at
your terminal, or in batch mode from a batch file. dal commands are the RDM runtime
functions (without the d_ prefix), plus a few additional commands. dal is an interpretive
language using variables that are defined as records, fields, database addresses, or
integers. The RDM functions are accessed by issuing statements very similar to the C
function calling conventions (with some exceptions, as noted below).
2.
11-1
Where the C calling conventions dictate a pointer argument, dal does not. Look at
the following example of an RDM C function call.
d_fillnew(person, &per);
dal supports character array constants, but not numeric constants. If you need to
pass a numeric value to an RDM function, you must first assign the value to a
variable, and then pass the variable to the function call.
Where a database constant is passed to an RDM function, you may use lowercase
letters (for example, person instead of PERSON).
11-2
d_dbuserid
d_delete
d_destroy
d_discon
d_disdel
d_fillnew
d_findco
d_findfm
d_findlm
d_findnm
d_findpm
d_freeall
d_initfile
d_initializ
d_ismember
d_isowner
d_keydel
d_keyexist
d_keyfind
d_keyfree
d_keyfrst
d_keylast
d_keylock
d_keynext
d_keyprev
d_keyread
d_keystore
d_lock
d_makenew
d_members
d_off_opt
d_on_opt
d_rdcurr
d_recfree
d_recfrst
d_reclast
d_reclock
d_recnext
d_recprev
d_recread
d_recset
d_recwrite
d_renfile
d_rlbclr
d_rlbset
d_rlbtst
d_setfiles
d_setfree
d_setkey
d_setlock
d_setmm
d_setmo
d_setmr
d_setom
d_setoo
d_setor
d_setpages
d_setrm
d_setro
d_timeout
d_trabort
d_trbegin
d_trend
d_wrcurr
11-3
def_rec, def_fld
Variables are defined in one of two ways:
Implicitly :
A variable can be created from the context of a statement. The statement below
causes a variable named "per" to be defined as the same type as the current record.
keyfind(pers_name,"Norm");
recread(per);
The next statement causes a variable named "addr" to be defined as a field with the
same type as the "pers_addr" field in the schema.
crread(pers_addr,addr);
Explicitly :
Two functions are provided to explicitly create and type record or field variables:
def_rec(rec_type,var_name);
or
def_fld(fld_type,var_name);
def_rec creates a variable (named var_name) that is defined with the same type as
the record specified by rec_type. def_fld creates a variable (named var_name) that is
defined with the same type as the field specified by fld_type.
11-4
abort
Exit dal immediately without closing database
Summary
abort;
Description
This command causes dal to stop immediately without closing the database. Any data
remaining in the RDM cache will not be written to the database. If you have created only
a few changes or additions (such that all the data fits into the cache), then none of your
work will be saved. However, be aware that indiscriminate use of the abort command
can lead to database corruption in the event that some changes get written to disk while
the remaining are lost when you abort. You must be concerned about this only if you are
not using transactions.
curr
Print the currency tables.
Summary
curr;
Description
This command causes dal to print out the currency tables, showing current database,
current record, and current owners and members for each set.
exit
Exit dal.
Summary
exit;
Description
This command calls d_close to close the database, and exit with a code of zero (that is,
call exit).
11-5
input
Read values into a variable.
Summary
input [(filename)] var_name [,var_name ]...;
Description
This command reads variable values from the keyboard or a file. If a variable is a record,
then the individual field values will be requested with separate prompts. If a file name is
specified for input, one line from the file will be read for each variable listed. File names
must be enclosed in quotes. Fields within a line are in fixed format with the following
lengths for different field types:
Char
int
short
long
10
float
10
double
10
Note that these widths are the same as those created by the print command.
Examples
open("tims","o");
def_rec(author,a);
input a;
fillnew(author,a);
input ("auth.imp");
fillnew(author,a);
Diagnostics
Errors can occur when input reads from a file and the line lengths in the file are
inconsistent.
11-6
print
Print values from a variable list.
Summary
print [(filename)] var_name [,var_name ...];
Description
This command prints one line of data to the terminal or to a file. If a variable is a record,
then the individual field values will be printed, with no spaces between the values, each
field width being determined by the field type (see table under input). Literal strings
may be included in the var_name list. If a file name is specified for output, one line will
be written to the file for each print command. File names must be enclosed in quotes. If
an output file is used within an input command, the file is re-opened for input.
Examples
open("tims","o");
findfm(author_list);
recread(a);
print "author:",a;
crget(dba);
print("author.lst") "dba:",dba,"author:",author;
rewind
Rewind (or reset back to the beginning of) a file.
Summary
rewind "filename";
Description
This command rewinds a file. If the file was opened for reading, then subsequent reads
will start from the beginning (that is, the first line will be reread). If the file was opened
for writing, it will be truncated and all previous data will be lost.
11-7
schema
Print schema information.
Summary
schema [spec];
Description
This command prints information about a database schema, including the record types,
field types, and set information. The schema command with no arguments produces a
list of the record names and set names. If spec is a record, then detailed information
about the record is printed, including a list of the fields, and the field information for
each field. If spec is a field name, then the type and length is printed. Additionally, if the
field is a key, then the kind of key, and the key file containing the key is printed. If spec is
a set name, then order, owner type, member type(s), and sorting information is printed.
whileok
Repeat statements while (db_status == S_OKAY).
Summary
whileok {
statements;
.
.
}
Description
This command functions in the same manner as the following C code:
while (db_status == S_OKAY) {
statements;
...
}
11-8
Examples
/* List all authors and their publications */
open("tims","o");
findfm(author_list);
whileok {
recread(a);
print "Author:",a;
setor(has_published);
findfm(has_published);
whileok {
recread(b);
print "Published:",b;
findnm(has_published);
}
findnm(author_list);
}
Timestamping functions
In addition,
11-9
Chapter 12
Database Editor (dbedit)
12.1 Introduction
The low-level database editor (called dbedit) allows any RDM database to be changed
and repaired by an operator who is trained in its use. Anyone who uses dbedit must be
familiar with the binary format of RDM data files as described in Chapter 14, "File
Formats and Dictionary Tables."
Caution: Improper use of dbedit can cause severe damage to a database.
dbedit, together with dbcheck, is most useful for finding and correcting errors in set
pointers and other overhead fields. Also, if any corruption exists in page zero of a data
file, dbedit can correct it. All other areas of database update or repair should be handled
through the runtime library, or through ida, dchain, or keybuild. For example, dbedit
can alter the value of a data field, but it is best to use ida for such a task because ida will
maintain database consistency (for example, by changing a key value to match the
changed field).
dbedit can perform updates to a data file regardless of the internal consistency of the file.
While the runtime library and ida must be able to successfully open a full database,
dbedit can operate on a file-by-file basis, and will not assume that any fields in the data
file are valid.
dbedit is line-oriented, instead of full screen-oriented. It is driven by a set of commands
that are read from standard input, and will print all information to standard output.
If a system record exists, it will become the initial current record. If no system record
exists, the initial current record will be database address [0:1]. If no records exist in file 0,
there will be no initial current record.
Database Editor (dbedit)
12-1
Because of the low-level nature of dbedit, it will not use the RDM environment variables,
such as DBDPATH. Nor will it use the rdm.ini file.
dbedit operates in one of two modes: interpreted or non-interpreted () modes. In the
interpreted mode, dbedit will display a records contents according to its type. It
displays set and member pointers in our standard database address representation. It
indicates whether optional keys are set, and displays field values in the format used by
ida. Navigation with dbedit means moving from one record to another. There will
almost always be a "current record." Edits made to a record will be written to the file
when the editor moves to a new current record.
The non-interpreted mode is entered by typing:
dbedit> edit hex
In this mode, no notion exists of current record. All data is displayed and edited as
hexadecimal bytes. All edits are written to the data file when the hex edit mode is ended.
12-2
dba
ts
opt
set
mem
fld
005344: 01 00
Record Type
INFO (1)
005346: 12 00 00 01
Database Address
[1:18]
005350: 00 00 00 00 00 00 00 00 00 00 00 00
005362: 00 00 00 00 00 00 00 00 00 00 00 00
005374: 01 00 00 00 29 00 00 01 29 00 00 01
005386: 02 00 00 00 1d 00 00 00 1e 00 00 00
005398: 01 00 00 01 28 00 00 01 13 00 00 01
005410: 00 00 00 00 00 00 00 00 00 00 00 00
005422: 64 62 30 30 31 00 00 00 00 00 00 00
00 00 00 00
005438: 46
61
67
50
00
00
00
6f
74
65
72
00
00
00
75
69
73
69
00
00
00
72
6f
20
6e
00
00
00
74
6e
56
63
00
00
00
68
20
6f
69
00
00
00
2d
4c
6c
70
00
00
00
47
61
20
6c
00
00
00
65
6e
31
65
00
00
6e
67
20
73
00
00
65
75
2d
00
00
00
72
61
20
00
00
00
005518: 50 72 65 6e 74 69 63 65 2d 48 61 6c
6c 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
005550: 31 39 38 35 00 00 00 00 00 00 00 00
005562: 00 00
Field PUBLISHER
Prentice-Hall
Field PUB_DATE
1985
Field INFO_TYPE
0
12-3
In addition, the set, mem, and fld keywords may be further restricted with the name of
one owner set, member set, or field.
The show command displays schema information, such as lists of sets, records, or keys.
show {fld [fldname] | file [filename] | key [keyname] | record [recname] |
set [setname]}
The show fld command lists all fields in the database, or, if fldname is given, shows that
field only.
The show file command lists the names of all files in the database, or, if filename is given,
shows the record types or key types contained in the file.
The show key command lists all keys in the database, or, if keyname is given, shows that
key only. Each key will be displayed as a field.
The show record command lists the names of all record types in the database, or, if
recname is given, shows that record type in the format shown in Figure 12-2.
Record Type: INFO (1)
Contained in file: tims.d02
Record Length: 220
Offset to Data: 78
Flags:
Fields:
unique key char ID_CODE[16]
char INFO_TITLE[80]
Flags: SORTFLD
char PUBLISHER[32]
char PUB_DATE[12]
int INFO_TYPE
12-4
12.4 Navigation
Movement through the database is accomplished with the goto command. This
command is used to set the current record and has several forms.
goto {dba | nextrec | prevrec }
goto {first | last} setname
goto {own | prev | next} setname
goto {file {filenum | filename}}
The most basic goto command allows you to set the current to a specified database
address as shown in the following example:
goto 2:345
The goto nextrec and goto prevrec commands allow you to move sequentially through
the current data file one record slot at a time. If the current record is the first record in a
file, then goto prevrec will produce an error message, and the current record will remain
unchanged. If the current record is the last record in a file, then goto nextrec will also
produce an error message, and the current record will remain unchanged.
Set pointers contained in the current record may be referenced to set the current record to
the first or last member using the goto first and goto last commands. The specified
setname must be a set for which the current record is an owner.
Set member pointers contained in the current record may be referenced to set the current
record to the owner (goto own), next (goto next), or previous (goto prev) member of the
specified setname.
If an invalid slot is formed by any of the above forms of the goto, an error is printed, and
no action is taken. It is legal to go to a database address that is beyond the current size of
a data file. Such a record is initially filled with zeroes unless the file already has readable
contents at the selected location. A warning message is printed if a selected slot is more
than two pages beyond the current end of the database (as indicated in page zero).
A data file may be selected with the goto file command. It may be selected by specifying
either a file number or file name. When a new file is selected, there will be no current
record. A goto nextrec will select the first record, and goto prevrec will select the last
record. Until there is a current record, only the page zero of the file may be edited.
12-5
12.5 Editing
The edit command is used to specify that a portion of the current record slot or page zero
is to be modified. All edits are performed in memory and are reflected in subsequent
display commands. An edited record is written to the database on execution of a
following goto or exit command. Unwanted edits on the current record may be
discarded by issuing a reread command. The complete syntax of the edit command is
shown below.
edit type
edit {first | last | count} setname
edit {own | prev | next} setname
edit dba
edit opt
edit {dchain | nextslot}
edit hex
12-6
The new value may be entered as a bit map (in hexadecimal), or as a list of the key names
(comma separated) that are to be set. Note that any changes performed here will require
the use of keybuild in order to make the data files consistent with the key files. In the
above example, both the publisher and info_type optional key flags will be set.
12-7
The hexadecimal editing mode is initiated by entering the edit hex command. When in
this mode, the prompt is changed to:
hex>
When hexadecimal edit mode is entered, changes that were made to the current record
are written to the database, and the current position in the file becomes the file address of
the current record. The changes made in hexadecimal edit mode are written to the
database by the end command. To cancel the changes and return to dbedit mode, enter
cancel.
After ending hexadecimal edit mode, the current record is returned to its original slot,
unless the current file position lies within a different record, in which case dbedit will
prompt you to see if your current record should be changed to this new position, or if
you want to return to the original current record.
Table 12-1 describes the commands that can be used in hex edit mode.
Table 12-1. Hex Mode Commands
Command
Definition
print N
+N
-N
=N
>N
<N
>>STRING
<<STRING
write STRING
cancel
end
help
12-8
12.6 Diagnostics
Illegal changes to a database will be screened, whenever possible, before they are made.
For example, if a record type is changed to a type not contained in the current file, the
change will not be allowed (except in hexadecimal edit mode).
Since dbedit allows updates to set instances, it will be important to know if a change has
corrected a problem in the chain of database addresses forming the set instance. For this
purpose, the verify command can be used to traverse an entire set instance to check
owner, previous, next, first, and last pointer consistency. It will perform a check similar
to that done by the dbcheck utility. The name of the set must be identified with the
following command:
verify setname
12.7 Miscellaneous
The exit command will end your dbedit session. If the current record was modified, it is
written to the database before exiting.
The reread command causes the current record to be read from the database. This has
the effect of canceling any updates made to the record in the buffer.
To eliminate the titles in the display output, the notitles command can be issued. The
resulting output is shorter, but readable to the experienced operator. By default, titles
will be printed. The titles command can be used to cancel the notitles command.
To eliminate the data fields in the display output, the nofields command can be issued.
The resulting output will only contain the overhead information (record type,
DB_ADDR, set pointers, etc.). This can be useful when the data in the record is correct,
but there are problems with the overhead information. By default, fields will be printed.
The fields command may be used to cancel the nofields command.
All database addresses and counts will be displayed in decimal format unless otherwise
requested. To change the display format, the base command may be issued.
base {10 | 16}
Only base 10 (decimal) and base 16 (hexadecimal) are recognized.
Any editor command or keyword may be abbreviated to a non-ambiguous set of
characters. If more than the required number of characters is entered as a command,
they must correctly spell the intended command. If an ambiguous set of characters is
entered, an error message is displayed stating that more characters must be entered.
12-9
12-10
Chapter 13
Maintenance Utilities
13.1 Introduction
This chapter describes the use of additional utility programs that are part of the basic
RDM system. These utilities support database maintenance and administration tasks.
They are listed in Table 13-1 below.
Table 13-1. RDM Utilities
Utility
Definition
datdump
dbcheck
dchain
initdb
keybuild
keydump
keypack
prdbd
dbclrlb
Maintenance Utilities
13-1
each record. If the -f option is specified, a formatted dump is printed but no hex dump is
printed. If none of these options are specified then the result is a full dump including
header, data fields, and hex record dump. The -r option can be used to instruct datdump
to dump only the contents of the record at the specified slot number, slotno, in datfile. The
report is written to file stdout and can be redirected as needed.
13-2
The -r# option reports every # percentage completed to file stderr. The -p# option sets
to # the number of pages in the cache for use by dbcheck. The -f# option sets to # the
number of open files for dbcheck. The -t option prints a traceback of the B-tree when a
key file inconsistency is detected. The -c option prints a count of the objects scanned in
the check.
Database inconsistencies will be reported with a message describing the nature of the
error and the database address of the record involved in the inconsistency.
Maintenance Utilities
13-3
The keybuild utility can be used to re-create the key files when there is a key file
inconsistency (for example, as reported by dbcheck). It can also construct new key files
after you have added or removed key attributes from fields in your DDL specification.
For example, if you make an existing key field a non-key, or change a non-key to a key
field in your DDL, you can run keybuild to rebuild the key files for the new schema.
You can also use keybuild to reassign key fields to different key files.
Optional keys are re-created by keybuild from the optional key stored bit flags contained
in the record header. To run keybuild, enter the following command line:
keybuild dbname
where dbname is the name of the database whose key files are to be rebuilt.
13-4
number of key slots per node to be left unfilled. If the key file is to be modifiable, num
must be 1 or greater (default is 1). If num is 0, the key fields contained in the key file
must all be declared in static records (see section 4.2.5, "Record Declarations").
Option -o opens the database in one-user mode; option -x opens the database in exclusive
access mode. Option -t specifies the location (path) of the temporary packed key file.
When finished, the original key file is deleted and the temporary packed key file is
renamed to the original key file name.
Option -b specifies the path where a backup of the original key files will be copied.
Option -k specifies the path where the packed key files will be placed. Option -p sets
pages in the RDM cache to num.
Maintenance Utilities
13-5
Chapter 14
File Formats and Dictionary Tables
14.1 Introduction
This chapter describes in detail the physical organization of RDM data and key files, the
structure of the runtime dictionary and currency tables, and some source code
implementation details. Although an understanding of this information is not necessary
to make effective use of RDM, it can be helpful in making better use of the system.
You may want to write utilities or functions that use this information. For example, the
file structure information could allow you to write utilities that can read through a database and update all occurrences of a certain field type. Please note, however, that while
the RDM runtime is in operation, it is not safe for an application to directly change any
database file other than through the use of RDM functions. Any updates to RDM files or
global tables during runtime execution are made at your own risk.
The information in this chapter can also be useful when making decisions about the
design of a database, since the trade-offs will be better understood. Licensed source code
users will be able to use this information to better understand the source code.
14-1
Database
File
Page
Slot
Length
4
4
Name
dchain
next
Contents
Head of the delete chain
Next available space in the file
timestamp
12
cdate
16
bdate
20
21
version
Pages are numbered, starting with page zero. Page zero is the file header, which only
stores file status information. No user data is stored in page zero of any file. Because of
this, the contents of page zero after the bdate field are subject to change without notice.
Table 14-1 identifies the contents of page zero.
14-2
Byte
offset
0
20
dchain
timestamp
next
R D M
5 . 0 . 0
cdate
bdate
[ 0 1 - F e b - 2 0 0 0 ]
header
update time
update time
update time
reserved
slots
slots
slots
14-3
Database Addresses
Every record slot within a database is uniquely named and addressed by a four-byte
structure called a database address. A database address is composed of a file number
and a slot number. Figure 14-4 shows the structure of a database address.
sl ot
file
14-4
Length
4
Contents
Count of members in set
12
member count
first member
last member
12
update timestamp
14-5
Length
4
Contents
Database address of owner record
4
owner
previous member
next member
14-6
file 3
owner record
slot 35
file 7
3:258
7:650
3:35
3:258
7:650
member record 2
slot 116
3:35
0:0
7:116
3:35
7:116
0:0
member record 3
slot 650
member record 1
slot 258
Length
2
Contents
Record type (index into record table)
10
(table)
variable
(table)
variable
(table)
variable
(table)
variable
Field values
14-7
Byte
offset
0
id
database address
creation timestamp
10
update timestamp
optional key flags
(table)
(table)
(table)
14-8
In Table 14-4, some offsets are defined as table values. These are offsets that vary
according to the record type. The first six bytes are fixed, always containing the delete
flag, record lock bit, record type and database address. These are altered when records
are deleted (see section 14.2.2, "Data File Organization").
The two highest-order bits in the record id are used for other purposes. The remaining
14 bits contain the actual record id. The highest order bit is turned on when the record is
deleted because the whole word is complemented (see the next section). The next highest
bit is turned on when a record lock is applied.
The record id is a number that is used as an index into the record table, which is detailed
in section 14.3, "Database Dictionary Table Structure."
The database address field contains the physical address of the record. This is used for
consistency checking. If the record is deleted, this field will contain the slot number of
the next record in the delete chain.
A timestamped record will contain a four-byte creation timestamp and a four-byte last
update timestamp. If a record is not timestamped, this extra space is not allocated.
If a set is not timestamped, the set pointers will be 12 bytes long. If it is timestamped,
they will be 16 bytes long.
One byte of optional key stored bit flags is allocated for every eight optional keys
declared in the record type. If no optional keys are declared, no space is allocated.
All portions of a record except the first six bytes are optional (a record is not required to
have any data fields).
Delete Chain
When a record is deleted, RDM complements the record id (the first two bytes), and
places the slot at the beginning of a linked list called the delete chain. A complemented
value will always be negative and have a one in the highest order bit, which is used to
detect that the record has been deleted.
A pointer to the head of the delete chain is in the header of each data file (the first four
bytes of page zero). Each slot in the list contains, in the third through sixth bytes, the slot
number of the next slot in the list. The last slot contains a null slot number (0).
14-9
Figure 14-10 shows the logical structure of a delete chain containing three deleted record
slots.
Page 0
Node Structure
A node, as used here, is a term that is synonymous with a page of an RDM key file. The
term is descriptive of its usage in a tree structure composed of nodes.
As in all RDM pages, a node starts with a four-byte integer that stores the time of last
update. Following that is a two-byte integer containing the number of currently used
slots in the node. The remainder of a node consists of a sorted array of fixed-length key
slots, followed by a single node pointer referred to as the orphan pointer.
The orphan pointer is the number of the node containing keys greater than the highest
key in this node. The maximum number of key slots that may be in one node is
determined by the slot size and node (page) size.
14-10
Length
4
variable
variable
Contents
Time of last update
Number of used slots in node
Slot space
Orphan pointer
Byte offset
0
used slots
key slots
14-11
Length
4
variable
variable
Contents
Pointer to child node
Key prefix
Key contents
Database address of source record
Byte offset
0
4
child node
prefix
key contents
database address
B-Tree Organization
As stated above, the used key slots in a node are sorted. Also, if a child pointer in a key
slot is not null, the node pointed to by the child pointer contains another set of sorted
keys, all of which precede the key value in the slot.
14-12
A B-tree starts with a root node. The root node is the entry point into a B-tree and is
always page one in a key file. Figure 14-13 shows a B-tree containing 11 nodes, with the
root node at the upper right.
4
0100
0200
0300
0400
4
0010
0020
0030
0040
2
-1 0201
-1 0203
-1
-1
-1
4
0110
0120
0130
0140
4
-1
-1
-1
-1
-1
0211
0212
0213
0214
4
0310
0320
0330
0340
0210
0220
0230
0240
3
-1 0222
-1 0223
-1 0226
-1
-1
2
-1 0231
-1 0234
-1
-1
-1
0410
0420
0430
0440
4
-1
-1
-1
-1
-1
0241
0243
0246
0247
14-13
The maximum number of key slots that may be filled in a node is computed as:
maximum slots = (page size - 10) / slot size
where 10 is the sum of the node header, containing the update time (four bytes), used
slots count (two bytes), and the orphan pointer (four bytes).
When the root node in the B-tree is filled to its maximum size, it will split into three
nodes: the original root node, which will contain only one key, and the two nodes at the
next level in the tree, which will contain the remainder of the keys from the original root
node.
As a B-tree grows, the non-root nodes will also be filled. As each node fills, it will split
into two nodes. Rather than leaving one key in the node and creating two child nodes (as
is done with the root node), the one remaining (middle) key is inserted into the node in
the next level up in the tree. Hence the two new nodes created by splitting one node are
at the same level in the tree, and the tree remains balanced.
If enough nodes at a lower level split, the root node will again become filled with keys,
and need to split. It will split again in the same way, leaving two nodes just beneath it.
This action increases by one the number of levels in the tree. This is important because,
at most, one database read is required per level in the B-tree. The more levels in a B-tree,
the more input and output are required in using it. Fewer levels are needed as more key
slots per node are available.
Just as the addition of keys to the B-tree causes additional nodes to be used, the deletion
of keys will eventually cause nodes to be emptied and discarded. During key deletion,
two nodes may combine into one node. This operation is effectively the inverse of a node
split. The unused node is placed into the key files delete chain.
Delete Chain
When a node is discarded during a key deletion, it is placed at the head of the delete
chain. As new nodes are required, they are first taken from the delete chain and then, if
the delete chain is empty, assigned from the end of the file.
The delete chain pointer is stored in the first four bytes of page zero in a key file. If the
delete chain header does not contain a null node pointer (-1L), it contains the node
number of the first node in the chain. Each node in the chain will contain the node
number of the next node in bytes four through seven. The last node in the chain will
contain a null as the pointer to the next node.
14-14
Figure 14-14 shows how a delete chain in a key file may appear.
page 0
-1
14-15
14-16
name
size_srt
byte offset
18
length 2
contents Number of sort table entries
name size_kt
byte offset
20
length 2
contents Number of key table entries
name
file_table
byte offset
22
length fallen = size_ft * sizeof(FILE_ENTRY)
contents File definitions table
name
record table
byte offset
fallen + 22
length rlen = size_rt * sizeof(RECORD_ENTRY)
contents Record definitions table
name
file_table
byte offset
fallen + rlen + 22
length fallen = size_fd * sizeof(FIELD_ENTRY)
contents Field definitions table
name
settable
byte offset
fallen + rlen + fallen + 22
length stolen = size_st * sizeof(SET_ENTRY)
contents Set definitions table
name
member table
byte offset
fallen + rlen + fallen + stolen + 22
length melon = size_mt * sizeof(MEMBER_ENTRY)
contents Set member table
name
sort table
byte offset
melon + fallen + rlen + fallen + stolen + 22
length strlen = size_srt * sizeof(SORT_ENTRY)
contents Set sort fields table
name
key table
byte offset
strlen + melon + fallen + rlen + fallen + stolen + 22
length size_kt * sizeof(KEY_ENTRY)
contents Compound key field table
The end of the table space in the dictionary file contains the record, field, and set names
in ASCII format. The names are variable in length and separated with new line charac-
14-17
ters. The size_rt record names are listed first, followed by the size_fd field names and
the size_st set names.
The runtime function, d_open, will allocate memory for the seven tables and read them
into memory from the dictionary file. The size of the tables as reported by ddlp is the
amount of required memory.
The dictionary tables for a database contain several references to each other. For example, the record table points to a range of definitions of fields within the field table. Each
field definition will also contain a reference back to the record table. Each table is an
array of structures, and each inter-table reference is an index into an array, which can be
used to directly access the definition. The following sections define the table elements,
and section 14.3.9, "Dictionary Tables Example," summarizes them with an example.
ft_name[FILENMLEN];
ft_desc;
ft_status;
ft_type;
ft_slots;
ft_slsize;
ft_pgsize;
ft_flags;
ft_name contains the file name in an ASCII string. The name may have path or drive
information embedded in it. The maximum length of the field, FILENMLEN, is 48
characters.
ft_desc contains the file descriptor returned by the open call. If the file is not open, the
value will be zero. This element is modified every time the ft_status element is modified.
14-18
ft_status contains the current status of the file; either open or closed. It is used during
runtime file opening and closing. The possible values are:
o = opened
c = closed
ft_type is the file type, with possible values:
d = data file
k = key file
o = overflow (transaction log) file
ft_slots contains the number of record or key slots on one page in this file.
ft_slsize contains the slot size, in bytes, of the slots in this file.
ft_pgsize contains the page size, in bytes, used for this file.
ft_flags is a bit map for the file options. See dbtype.h for a list.
rt_file;
rt_len;
rt_data;
rt_fields;
rt_fdtot;
rt_flags;
rt_file is a reference to the file table entry that defines the file containing this record type.
rt_len is the total length of the record. If this record is the largest in its file, the value of
file_table[rt_file].ft_slsize is equal to rt_len. This value is required, however, because
files can also contain smaller records within the full slot size.
rt_data is the offset from the start of the record to the first data field.
rt_fields is the index of the first field definition in the field table.
rt_fdtot is a count of the number of fields in this record type.
14-19
rt_flags is a bit map for the record options. See dbtype.h for a list.
14-20
The g (grouped), and k (compound key) fields have no data of their own, as the rest
of the field types do.
A group of fields is ordered in a specific way. First, the group definition occurs, then
the elements of the group occur, each having a flag set (see fd_flags) indicating that
they are group elements. The group ends when there is a field without this flag set,
or at the end of the table.
If the field is flagged as a compound key field (k), it will always follow the normal
field definitions in a record. This compound key field redefines the purpose of
fd_ptr so that it points to an entry in the key table rather than marking the byte offset
from the start of the record to the contents of this field. (see section 14.3, "Database
Dictionary Table Structure").
fd_len is the length of the field in bytes. If the field is an array, this length is the basic
data type length times the dimensions of the array.
fd_dim[MAXDIMS] is a three-element (MAXDIMS is three) array containing the values
of any declared dimensions. By default, all are zeros. If one dimension is declared with
the field type, then fd_dim[0] will contain that value, and the others will remain zero.
If this field is a key field (fd_key is "d" or "u"), then fd_keyfile is an index into the file
table that references the key file that contains this field.
fd_keyno is the key prefix number, which is stored in the first two bytes of each key of
this type (see section 14.2.3, "Key File Organization").
fd_ptr is the byte offset from the start of the record to the contents of this field in a
record. If this field type is k (compound key), this is an index into the key table of the
first field definition of the compound key.
fd_rec is an index into the record table of the record that contains this field.
fd_flags is a bit map for field options. The high-order six bits store non-zero optional
key numbers. The low-order 10 bits store the options for the field.
14-21
14-22
mt_record;
mt_mem_ptr;
mt_sort_fld;
mt_totsf;
mt_record is an index into the record table of the member record type.
mt_mem_ptr is the offset, in the record, of the member pointers.
mt_sort_fld is an index into the first entry of the sort table of a sort field definition, if the
set is sorted.
mt_totsf is the number of sort table entries used by this set member. This contains zero if
the set is not sorted.
se_fld is an index into the field table of one field definition involved in the field sort.
se_set is an index into the set table of the set type that owns this sorted list of members.
14-23
the compound key table. This range of entries is used to form a single key from the fields
referenced by the key table entries.
Each entry in this table refers to the compound key and an associated field, the position
of the field in the key, and whether the field is ascending or descending.
typedef struct {
DB_SHORT
kt_key;
DB_SHORT
kt_field;
DB_SHORT
kt_ptr;
DB_SHORT
kt_sort;
} KEY_ENTRY;
kt_key is an index into the field table of the compound key definition in which this entry
is included.
kt_field is an index into the field table of a field used in the compound key.
kt_ptr is an offset from the start of the actual key to where the contents of this field
begins.
kt_sort is the sorting order of this field. The possible values are:
a = ascending
d = descending
14-24
The prdbd utility can be used to print the contents of the dictionary tables. The report
from a run of prdbd -c on the above DDL specification is shown below.
14-25
-----------------------------------------------------------------FILE TABLE:
file des sta type slots sl sz pg sz
---- --- --- ---- ----- ----- ----0
0
c
d
15
64 1024
1
0
c
k
20
50 1024
flgs
---0000
0000
name
---data
keys
-----------------------------------------------------------------RECORD TABLE:
[#]first
tot
[#]record
file
len data
field
flds flags
-------------- ---------- ---- ---- --------------- ---- ----[ 0]MANAGER
data.d00
63
19 [ 0]EMP_NO
3 0010
[ 1]DEPT
data.k01
34
18 [ 4]TITLE
3 0000
-----------------------------------------------------------------FIELD TABLE:
key
key record
[#]field
key type len file num offset [#]record
flags dims
--------------- --- ---- --- ------ --- ------ -------------- ----- ---[ 0]EMP_NO
d
l
4 keys.k
0
19 [ 0]MANAGER
0000
[ 1]LAST_NAME
n
c 20
0
23 [ 0]MANAGER
0010 [20]
[ 2]FIRST_NAME
n
c 20
0
43 [ 0]MANAGER
0010 [20]
[ 3]NAME
d
k 40 keys.k
1
0 [ 0]MANAGER
0400
[ 4]TITLE
n
c 10
0
18 [ 1]DEPT
0001 [10]
[ 5]LOC_CODE
n
i
2
0
28 [ 1]DEPT
0001
[ 6]BUDGET
n
f
4
0
30 [ 1]DEPT
0000
(Continued)
14-26
(Continued)
-----------------------------------------------------------------SET TABLE:
[#]owner
own ptr first total
[#]set
order
record
offset member members flags
--------------- ----- -------------- ------- ------ ------- ----[ 0]MANAGES
a [ 0]MANAGER
7
0
1 0000
-----------------------------------------------------------------MEMBER TABLE:
mem ptr
mem #
[#]record
offset
----- -------------- ------0 [ 1]DEPT
6
# of 1st
sort fld
-------0
total
sort flds
--------2
-----------------------------------------------------------------SORT TABLE:
sort #
-----0
1
[#]field
--------------[ 4]TITLE
[ 5]LOC_CODE
[#]set
--------------[ 0]MANAGES
[ 0]MANAGES
[#]compound
key field
--------------[ 3]NAME
[ 3]NAME
[#]component
field
--------------[ 1]LAST_NAME
[ 2]FIRST_NAME
ptr
--0
20
order
----a
a
Figure 14-15 shows the values that would be contained in the dictionary tables for this
database. The values correspond to the element definitions contained in the above
sections. The table interrelationships are also shown.
14-27
file_table:
record_table:
0: data 0
1: keys 1
d
k
c
c
0: 0
1: 0
15 64 1024 0000
20 50 1024 0000
file_table
entry
set_table:
0: a
63 19 0
34 18 4
record_table
entry
number of
member_table
entries
first
member_table
entry
first
sort_table
entry
field_table:
0:
1:
2:
3:
d
n
n
d
l
c
c
k
4 0 1
20 20 0
20 20 0
40 0 1
0
0
0
1
19 0
23 0
43 0
0 0
0000
0010
0010
0400
0
0
0
18 1
28 1
30 1
0001
0001
0000
key_table entry
(compound key fields only)
member_table:
0: 1 6
0 2
record_table
entry
number of
field_table
entries
first
field_table
entry
1 0000
3 0010
3 0000
4:
5:
6:
number of
sort_table
entries
n
n
n
c
i
f
10 10 0
2 0 0
4
0 0
record_table
entry
file_table entry
(key fields only)
key_table:
sort_table:
0:
1:
4
5
0:
1:
0
0
set_table
entry
field_table
entry of
compound key
field_table
entry
3
3
1 0
2 20
a
a
field_table
entry of
data field
14-28
14-29
14-30
The record type constants are used to index into the record table. As defined, they have a
value of RECMARK (10000) added to them, which must be subtracted if the record table
is to be referenced from it.
The field type constants index into the record table and an offset within the record type.
The record type is encoded into the field type constant as follows:
field type constant = record type * 1000L + field offset
The 1000L is defined by a constant named FLDMARK. Because the field type constant
includes the record index and field offset, new field types can be added to existing record
types without changing the field type constants for other record types. The field type
constant can be decoded as follows:
record id
offset
field type
The set type constants index into the set table. As defined, they have a value of
SETMARK (20000) added to them, which must be subtracted if the set table is to be
referenced.
14-31
dproto.h
dproto.h is automatically included by vista.h. To use proto.h (which you should only
include if you are making calls to system-level RDM functions) you must include
dbtype.h, which in turn requires you to first include vista.h. The function prototype files
define the function type. If you define the constant LINT_ARGS, then the prototype files
will also define the types of the arguments to the functions. If your C compiler does not
support the const keyword (part of the ANSI standard) then define the constant
NO_CONST.
The header file vista.h contains the definition of two macros called P1 and Pi, which
expand argument definitions if the LINT_ARGS switch is defined. If LINT_ARGS is
not defined, the macros turn into null comments. This allows compilers that do not yet
recognize function prototypes to compile RDM source code.
14-32
If this definition is compiled with the appropriate compiler labeling switch turned on,
INTERNAL_FIXED becomes pascal. If WINDOWS is turned on, DB_FAR becomes far.
If LINT_ARGS is turned on, P1(CONST char DB_FAR *) becomes const char far *. If
NO_CONST is not defined, CONST becomes const. Thus, the compile line below,
cl ... -DLINT_ARGS -DWINDOWS -DMSC ...
Programming a DLL for Windows requires the use of the Pascal convention. Other
compilers that support the convention can be made to use it also with some simple code
changes in vista.h.
14-33
Glossary of
Database Terms
Alias
Application Programming
Interface (API)
Application
Argument
Arithmetic expression
Arithmetic operator
ASCII file
Audit file
Authorization
B-tree
Back up
Buffer
Cache
Case sensitive
Character
Character string
Gloss-1
Checkpoint
Client
Column
See Field.
Command
Commit
Compound key
Concurrency
Configure
Connect
Consistency
Constant
Cooperative processing
Gloss-2
Currency tables
Current database
Current member
Current owner
Current record
Data dictionary
See Dictionary.
Data field
See Field.
Data file
Database
Database administrator
Database address
Database Definition
Language
Database management
system
Gloss-3
Database server
DBMS
DDL
Deadlock
Default
Delete chain
Delimiter
Dependent table
Dictionary
Disconnect
DLL
Duplicates
Engine
Entity
See Field.
Environment variable
Exclusive Lock
Gloss-4
Expression
Field
File
File server
Floating point
Function
Grant
Hierarchical database
model
Identifier
Index
Key file
Gloss-5
Key scan
Key
Keyword
LAN
Lock
Logging
Logical operator
Many-to-many relationship A relationship between two record types, A and B, such that
for each occurrence of type A, there are many related
occurrences of type B and, for each occurrence of type B,
there are many related occurrences of type A. In RDM,
many-to-many relationships can be implemented using two
one-to-many sets through a third, intermediate record type.
Mathematical function
Member of set
See Set.
Member pointer
Multi-user
Gloss-6
Navigation
Node
Null
Numeric constant
ODBC
One-to-many relationship
Open Database
Connectivity
Operator
Owner of set
See Set.
Page
Password
Path name
Picture
Gloss-7
Pointer
Precedence
Privilege
Process
Queue
Read lock
Record occurrence
Record type
Record
Recovery
Redundant data
Referential integrity
Gloss-8
Restore
Revoke
Rollback
Root node
Runtime system
Scale
Schema
Set
Set occurrence
Set pointer
Gloss-9
Set scan
Single-user
Slot
Static record
Stored procedure
String
String delimiter
Synchronization
Syntax
System record
Table
Timeout
Timestamping
Transaction
Gloss-10
Unicode
Value
Variable
Virtual memory
Gloss-11
Index
#define constants 4-19
#include 5-42
A
-abcc
ddlp argument 4-21
-amsc
ddlp argument 4-21
abnormal termination
recovery 6-5
abort transaction 6-3
access
methods
indexed 2-2, 2-9, 2-10
network 2-10
sequential 2-10
performance optimization 4-36
requirements 6-6
sequential 5-31
alignment of data fields 4-11, 4-17
alignment of ddlp 4-20
archive logging 5-14, 6-7
disabling 5-14
arrays 4-10
and dal 11-9
ASCII file transfer 10-1
ASCII text
converting with dbimp 10-21
exporting with dbexp 3-4, 10-3
importing with dbimp 3-4
redefining with RDM 5-50
attribute 2-8
defined 2-1
automatic recovery 6-6
C
cache 5-13, 6-4
overflow 6-5
case insensitivity 5-50
Centura
Web home page 1-5
character arrays 4-10
character sets
international 5-50
checking account example 2-1, 2-6, 2-8,
4-3, 4-6, 4-11, 4-17, 4-18, 4-23, 5-19,
5-24
key fields 4-23, 4-25
record declarations 4-8
timestamping 4-4
child pointer 14-12
class 4-13
CLOSEFILES option 5-14
closing a database 5-4
coded fields
validation of 5-19
collating sequence 5-50
column 2-8
defined 2-1
comments
RDM DDL 3-9
commit 6-1, 6-6
compiling 3-19, 4-17
compound key 4-12, 4-24, 5-22
declaration syntax 4-12
table 14-23
connect command 8-10
consistency of a database 13-2
console
definition of 3-5
context switching 5-47
control information
B
B-tree
indexing method 2-2, 4-22
node 14-10
root node 14-12
Index-1
Index-2
Index-3
d_stscr 7-15
d_stscs 7-15
d_timeout 7-5, 7-7, 7-9
d_trabort 6-3, 8-26, 8-40
and lock bit settings 7-17
d_trbegin 6-3, 6-4, 7-10, 8-26, 8-40
d_trend 6-3, 6-4, 8-26, 8-40
d_wrcurr
and dal 11-9
dal 3-7, 11-1
commands
abort 11-5
curr 11-5
def_fld 11-4
def_rec 11-4
exit 11-5
input 11-6
print 11-7
rewind 11-7
schema 11-8
whileok 11-8
definition of 3-3
variables 11-4
data
entry 7-19
exporting 10-22
importing 10-5, 10-22
integrity 7-2
model 2-3
hierarchical 2-8
network 2-4
relational 2-8, 2-9
transfer 10-1
data fields
declaration syntax 4-9
defined 2-1
key 4-10
data file 2-11, 4-5
dump utility 13-1
structure 14-3
database
control functions 5-2
defined 2-1
Index-4
errors
preventing 7-13
initialization 3-2
modular 4-31
repair of corrupted 12-1
temporary 4-31
Database Access Language. See dal
database address 4-10, 5-15, 14-4
file number 14-4
slot number 14-4
Database Definition Language. See
DDL.
database design 4-1, 14-1 to 14-33
example 4-36
logical design considerations 4-22
multiple database usage 4-31
physical design considerations 4-32
use of keys 4-22
use of sets 4-26
database editor 12-1
database initialization 5-11, 13-3
database manipulation 5-1
database models
combined 2-9
hierarchical 2-8
network 2-4
relational 2-4
database name 4-3
length of 4-3
truncation 4-3
database statement 4-3
database table 14-29
database transfer 10-22
restrictions 10-22
datdump 5-51, 13-1
and Unicode 9-8
definition of 3-5
DB_ADDR 4-10, 8-11
db_QUERY 1-2
and Unicode 9-7
definition of 3-5
in context switching 5-48
db_REVISE 1-2
Index-5
system 5-42
user 5-42
escape characters 10-6
examples
checking account 2-1, 2-6, 2-8, 4-1,
4-3, 4-4, 4-6, 4-8, 4-11, 4-17, 4-18,
4-23, 4-25, 5-19, 5-24
DDL specification 4-1
family information database 10-7
full family tree database 10-14
library database 10-9
m.o. (modus operandi) 5-20
tims 3-8, 4-36, 5-4, 5-16, 5-22, 5-26,
5-35, 5-39, 6-1, 7-4, 7-5, 7-19, 7-21,
7-23, 7-24, 7-25, 7-26, 10-3
vehicle make 4-23
exclusive locks
freeing 7-9
exporting
and importing 10-22
definition of 10-1
maintaining set information 10-4
string conversion 10-4
E
edit command 8-9
efficiency
data entry 7-19
tips 4-33
end transaction 6-3
entity
defined 2-1
environment
control functions 5-9
files 5-4, 5-12, 5-14
path name 5-5
variables 5-5
error codes
function status codes 5-42
system error codes 5-42
user error codes 5-42
errors
reporting 5-42
F
failure
recovery 6-5
family information database 10-7
family tree database example 10-14
field 4-10
declaration 4-10
defined 2-1
field name constants 4-20
field names
restrictions 4-10
field table 14-20
file 2-1
declaration 4-5
defined 2-1
dictionary 5-5
format 10-1
header 14-2
identifier 4-5
Index-6
initialization 5-12
key file structure 14-10
lock types 7-7
locking 7-4
tips 7-18
names 4-5
maximum length 4-5
node 14-10
number open 5-12
page 14-10
length 14-2
sizes 4-36
report
structure 4-34
slot size 14-3
structure 4-32, 14-2
structure report 4-16, 4-34
substitution of 5-10
table 14-18
transfer 10-1
file menu 8-30
find functions 5-24
function prototyping 14-32
function statuses 5-42
G
GLOBALALLOCS 5-15
grouped lock request 7-10
H
header file 3-7, 3-11, 4-7, 4-18, 4-20,
14-30
file name 4-3
hex edit mode 12-2
hexadecimal editing 12-2
hierarchical database model 2-8
I
ida 3-7, 5-51, 5-55, 8-1
database access commands
Currency 8-18
Free 8-23
Lock 8-22
Misc 8-24
Record 8-14
Set 8-17
Transaction 8-21
definition of 3-3
field editing 8-10
function cross-reference 8-25
record manipulation functions 8-14
utility commands
Access 8-6
Close 8-7
Initialize 8-7
Open 8-6
Parameters 8-7
Quit 8-8
timestamping 7-13
import specification
language 10-7
statements 10-8, 10-9
connect 10-10
create on 10-10
find on 10-14
update on 10-14
importing
and exporting 10-22
definition of 10-1
string conversion 10-4
importing data
restrictions on ASCII files 10-2
index, definition 2-2
indexed access 2-10
init command 8-9
initdb 3-7, 3-12, 13-3
definition of 3-2
integrity of data 7-2
Interactive Database Access. See ida
international character sets 5-50
intersection record 4-27
K
keep locks 7-10
key
defined 2-2
duplicate 4-10
Index-7
field 2-11
maximum length 4-10
optional 4-10
placement of 4-33
primary 5-33
rebuilding 13-3
retrieval 5-18
scan 2-2
unique 4-10
usage 4-22
usage for data retrieval 5-19
key file 2-2, 2-11, 4-5
dump utility 13-4
node 14-10
packing utility 13-4
page 14-10
structure 14-10
key menu 8-32
keybuild 13-3
definition of 3-4
keydump 5-51, 13-4
definition of 3-5
keypack 13-4
definition of 3-4
keys
compound 5-22
validation of coded fields 5-19
keywords
printing with pr_keywords 5-28
L
leaf node 14-12
library database example 10-9
linked list 4-14
linking 3-19
LINT_ARGS 14-32
lmclear
definition of 3-3
LocalAlloc 5-15
lock 7-1
advisory 7-17
downgrading 7-10
exclusive
Index-8
freeing 7-9
file 7-4, 7-18
granting 7-10
grouped request 7-10
manager 7-3
read 7-8
record 7-17, 7-23
request 7-3
request packet 7-20
static record 7-12
status 7-7, 7-11
timeout 7-9
types of 7-7
upgrading 7-10
write 7-9
lock bit
and d_trabort 7-17
clearing 7-17
setting 7-17
testing 7-18
within transactions 7-17
lock manager 3-7
and Unicode 9-9
definition of 3-3
single-user modes 7-27, 7-28
LOCK_REQUEST 7-11
locks menu, free types option 8-41
log file 5-9, 7-3
M
m.o. (modus operandi) example 4-24,
5-20
many-to-many 4-26, 5-25, 5-26
and intersection records 4-27
member 2-4 to 2-7, 4-14
pointer 14-5
space requirements 4-32
member table 14-23
memory 3-10, 4-16, 5-4, 5-13
Microsoft Windows
allocating memory in 5-15
modification of data 5-38, 7-22
modular database 4-31
Index-9
defined 2-1
lock 7-23
lock bit 7-17
and d_trabort 7-17
clearing 7-17
setting 7-17
testing 7-18
within transactions 7-17
locking 7-17, 7-23
name
case sensitivity 4-7
constants 4-20
occurrence 2-1
new 5-34
placement of 4-33
static 7-12, 7-27
structure 14-7
table 14-19
type 2-1, 2-10, 2-11
unused space 4-35
variable-length 4-29, 5-26
record menu 8-36
records dialog box 8-28
recovery 6-1
automatic 6-6
external 6-6
redundant data 2-6
data access performance 4-39
eliminating 2-10
relatedto 4-10
relational database model 2-8, 2-9
retrieval
of data 5-18
of text 5-31
rlb_status 7-18
ROM 1-2
root node 14-13
row 2-8
runtime
control 5-12
library 3-7, 3-19, 5-1
definition of 3-3
Index-10
S
S_DBOPEN 5-4
S_DELETED 7-8, 7-14
S_DUPLICATE 3-16, 4-10
S_EOS 5-37
S_INVDB 5-44
S_LOCKED 7-17, 7-18
S_NOMEMORY 5-4, 5-13
S_NOSPACE 5-42
S_NOTFOUND 3-16, 5-42
S_NOTLOCKED 7-16, 7-17
S_OKAY 5-1, 7-14
S_RECOVERY 6-7
S_TIMESTAMP 7-16
S_TRACTIVE 6-3
S_TRNOTACT 7-9
S_UNAVAIL 7-4, 7-9, 7-17
S_UNLOCKED 7-18
S_UPDATED 7-14
schema 2-2, 3-1
changing 10-22
diagrams 2-5
separator characters 10-6
sequential access 2-10
sequential access functions
and currency 5-32
listed 5-31
set
declaration syntax 4-14
defined 2-4
many-to-many 4-26, 5-25, 5-26
member 2-5
multiple member 4-30, 5-26
name constants 4-20
navigation 5-15, 5-18, 5-23
ordering 4-14, 10-22
owner 2-5
pointer
space requirements 4-32
usage 4-26, 5-23
set member pointer 14-5
set menu 8-34
Index-11
d_open 5-8
dal 3-3, 3-7, 11-1
datdump 3-5, 13-1
dbcheck 3-4, 13-2, 14-12
and dbedit 12-1
dbclrlb 13-5
dbedit 3-4, 12-1
and environment variables 12-2
base 12-9
display 12-2
edit 12-6
exit 12-9
goto 12-5
help 12-2
hex 12-7
nofields 12-9
notitles 12-9
reread 12-9
show 12-4
verify 12-9
dbexp 3-4, 10-1, 10-3
with dbimp 10-22
dbimp 3-4, 10-1, 10-5
and set ordering 10-22
with dbexp 10-22
dchain 13-3
Index-12