ISAM REFERENCE 


Version 2.10 


February 3, 1995 


(C) Copyright Psion PLC 1990-95 


All rights reserved. This manual and the programs referred to herein are copyrighted works of Psion 
PLC, London, England. Reproduction in whole or in part, including utilization in machines capable of 
reproduction or retrieval, without express written permission of Psion PLC, is prohibited. Reverse 
engineering is also prohibited. 


The information in this document is subject to change without notice. 


Psion and the Psion logo are registered trademarks, and Psion, Psion MC, Psion HC, Psion Series 3, 
Psion Series 3a and Psion Workabout are trademarks of Psion PLC. 


TopSpeed is a registered trademark of Clarion Software Corporation. IBM, IBM XT and IBM AT are 
registered trademarks of International Business Machines Corp. Microsoft and MS-DOS are registered 
trademarks of Microsoft Corporation. Apple and Macintosh are registered trademarks of Apple Computer 
Inc. VAX and VMS are registered trademarks of Digital Equipment Corporation. Brief is a registered 
trademark of Underware Inc. Psion PLC acknowledges that some other names referred to are registered 
trademarks. 


Ea Se eee 


Contents 


OO eee 


DV TREODUCHON os. cre ciacsesdcztestsavis besksace sabteeentce ce ic thee es ca i 1 
OVER VICW Pres cite ccc vapciu part tetne sacra see trt narescet em vennescattes’ ieitins dike one vessdaice 1 
MPS TSAIM NR ALY wigiie ven tersestode Gaavaaabsiavenssniavedincy ss wersaye Secseevoearcceucokiacheeks 1 
DBF IGS aed drauu gar tha nis det ane oe di dewey oenueatgtiad tenes Grateiunew ne ules honttcat Nt 2 
PICS arsdeecceantd veunasarcwacmecuransoisnagteusias seve canueee paises ioc Seales: Staab ected 2 
NUMBER OR FECORES oo ceca. ailoc feel awesseopeissavvapiaveréuecs taibesctbiacteesGshealdleccs 2 
Bethe ee ES: 3550 sins wands cocuesuestn fa catpeiaaile Uened tamale noes davik ov caus tstdewonc wie orasc oe 2 
BIOCK DUNERIMG i cnac acces anstyes cuyinassuestiea Madeausccauan innate es ded cuhaesiontiecses ee 2 
ICY CES CHE OM ernst ceceatecsatovissanasdelsadendsastaccs dbversducenccesageiner foxtevente osha. 2 
DIZELOMBaEG MNOS ic ascaveusdacsadeacensetarasrsuaiiooievdediduamesbestiuree eicanacaniclncs 3 
Interface to the ISAM library.............sscssssscecceccssseccccessccccusessceseeceecscccesees 3 
BOAO NG ION Esai neecestans avsseccnensaguatvcsewaseoss iiaceebataoaeetsvaeeickecuicenicet 3 
Creating am (SAM :OB)OCE si. 5cc.iscsnssasteecisadeiWevevasevisesss aide keaseaeavedeaesiseeécck 4 
Calling (ISAM MUNCLONS 4.iscissye2sedoacasuedsaseerdeweesecedansdssesesahuocdcvercecsa covers 4 
Destroying the ISAM object..............sssssssccssssscceceseccccenesecsececsueecceceuces 4 

UV PUG ASE: si seca lane vcuuwetded osanes des adynnsvauseuremettleauon secon Meraeloecate cic ccs 4 
EXAMPIG PROGQHANTS « wosascredececslsoueiaavecadatheuade nee nckass ws aeer sea tee laieclbeccsseecaninl 5 
Building a complete (dense) index.............cssssscccsssssccssssecececsesseueceneesees 5 
Building a selective (sparse) index .............csssescccssecccsasscececcescccecceseeese 7 


@ ISOM FUNCTIONS sve sestes seo choc 05 Secudeutce se buscdvscaudbasduvacbecvuaceacnnssd sata lice hedabeseae ek 9 

GE MSN al MU MCHONS vases connec cu seiisenladeubuaycatedonsouauueedisoves ss cusSaceavlncsiea oo licetaas 9 

Initialise an ISAM object (O_IS_INIT)...........ssssssecesscscccceecececccaueseccesecees 9 
Destroy an ISAM object (O_IS DESTROY) ............ccccecsesessessssecececeesesees 10 
Set the field definition (O_IS SET FIELDDEF) .............cccccsscccccesesseuseneees 10 
Get the field definition (O_IS_GET_FIELDDEF) Sevestecdaied sida oeeRhuecdacvesieekeest 11 
Set the key definition (O_IS SET _KEYDEPF) .............ccccsscsssseecccceeesecccuees 11 
Get the key definition (O_IS_GET KEYDEP).............ccccsesssseccccesesessueecees 13 
Put a value into a field (fom IS PUT CSRIEUD) 5 in sbavecsuedessscecdsscssevesceccetestacies 13 
Get a value from a field (Oi IS GET GEIBUID). cccsisanseescecincsacoansovsseosantoeecs 13 
Get the type of a field (0 _[S GET TYPE) au incnetecnrenecmestntsesetireecuoeroeieces 14 
Get the address of the record buffer (0. IS GET_RBUP)..............cccccesees 14 
Set the radix for number conversion (0 _| AS “SET RADIX) eiseaaiWwanstanvasettaxns 14 
Set the text format for DOUBLES (O_ IS SET _ RADIX) daacaduece sbaveteveeauasaess 14 
Add ancventry (O° 1S ADD) ssccs er cecccais sts Sons Cissvepwes doves sevavievec bevdeccecetovecess 14 
Erase an entry (O IS ERASE).......ccsssccsssssccssscsssscsssssscersonecsececesssessens 14 
Update an entry (O_IS_UPDATE)..............cccsseceeseccccsscceccercecseuececeeevecs 15 
ata HIG FUMCNONS tise ssranii scp aes terebeeaiasdaxedtagd touted aleuumcvensuenteccteeccni dfs 15 
Open a data file (O_IS DOPEN)..............ccccsssccssstceseconsesccenseneccessceeeesce 15 
Flush data file buffers {0 IS DFELUSH) ic scctsacsckiv cesta dtsnsberscxevseovacdescs vases 15 
Get sizeof data Tile (0! IS SIZE) 0. sis.ssccsstersssivacnssavenerenesassieccdeieedsc se 15 
Read first data record (O_IS DFIRST)...........cccccssescsessescecstessecsccescesscs 15 
Read next data record (O_ Is” DN EXT) idavedeves dense de deeocsedeeses csavadetectictis 16 
Index file FUNCTIONS ...........ccccvesseccccccscccoretsnscseresstesceetaccsecesconsevscececeecceccee 16 
Open: aniimdex: (OISIOREN) esl 5Ge inf Gscws toecsshasasinea sau celeeesadeneaaceeer ghee ecs 16 
Close an index (O_ [SISO SEP cicaesterncaveastyt.temadlen eae basal ea, 16 
Flush index file buffers (OCIS MBLUSH) 5 secs fea iee sicnadeavesseidebdeecnedeaathersoes 17 
Set and get index flags (O_ Is” SIREAGS)ocacsetets cates docuisicdvsntsevese Sensesheons 17 
Specify a filter method (O_ is PANELISTS estbiadt 2s coe bt os ssi hind vaneatee tocnaeannee: 17 
Specify a duplicates method (om ISSIDUP) 23. oss tacdesrvceacs vavanand each catceeeivee 18 
Bulle an: index: (OIS | IRONED). s.0 ies idcubeeexdssvacecanederecvesacadecaverlinetlccacesecs 18 
Build an index from sorted data (OMS *IQBUIED) 2. cits seccovedecesssaciceeees ecees 18 
Add an index entry (O_ ISBIA DD) Seth nett se create ante ata, «St ee 19 


ISAM REFERENCE 


i 


Erase an index entry (O_IS_IERASE).............cssscsssscccevscorsvecesesecesscconees 19 
Erase all index entries (O_ is wa IERASEALL) wok. ssscscccecatintees .cmresceterreeees 19 
Get the number of index entries (OBISTIGOUNT) ciciiniressccnteecces ceStesceieees 20 
Get size of an index file (O_IS | ISIZE) T Sawa case cdces salyseuieess se coe acbeascMeereeneeee 20 
Find a record using an index (ORISAIFIND) 6.25.0 c.uscucsstih ce ee cee: 20 
Read next indexed record (O_ is_ _INEXT) G Saline deen ve sovaatedaw asus eoeeeieteceeanete 21 
Read previous indexed record (O4 ISSIBACK) & ia. cckscc codes cei acteatettectseoes 21 
Read first indexed record (O_IS IFIRST) Le teet eats te cectatrauacat et ecetscaseeereones 21 
Read last indexed record (O_ 1s  ILAST) SCOOT TET ESTE EC CCLEER SET eee 21 
Read current indexed record (O_ ISSIGURRENT) oa cs escscaseisdccccscenstesceuests« 21 


CHAPTER 1 


INTRODUCTION 


SSS_—— SSS ee eae ee a ee ey 


Overview 


This manual describes the Indexed Sequential Access Method (ISAM) dynamic library for SIBO 
machines (HC, $3, S3a and MC). 


The ISAM library provides a powerful set of functions for rapid and efficient access of Database files 
(DBF files). Such files consist of multiple records containing one or more structured fields. They can be 
created, maintained and accessed from OPL programs and database applications on the HC, S3, S3a and 
MC (see the Database Files chapter of the PLIB Reference manual). 


The ISAM library uses B-tree index files to provide rapid and efficient indexed and sequential access to 
DBF files. It is the most widely used form of index file simply because it is the most efficient general 
method of accessing database files. In brief, B-tree index files minimise the number of disk accesses per 
retrieval - manipulating data already in memory is much faster than accessing the disk - whilst ensuring a 
well balanced index i.e. no one retrieval requires an inordinately large number of disk accesses (for 
details see for example Structures, by Michael J.Folk and Bill Zoellick, published by Addison-Wesley 
Publishing Company). 


The ISAM library 
The ISAM library functions provide the following: 


= Fast record retrieval on a key. For example a DBF file can be searched for all records containing 
the key "Smith". 


= Very little degradation of the speed of retrieval as the number of records increases - even large 
DBF files can be manipulated with relative ease. 


= Sequential access to records that are ordered by the key (that is, next, back, first and last). For 
example a record with key "Smalley" that precedes a current record with key "Smith" can be 
rapidly found using the 0_I1s_1BACK function. 


= The ability to see a selection of the records in the file (by constructing a selective or Sparse 
index). For example all records containing the key "Smith" can be viewed. 


= Access to record fields with automatic conversion from numbers to text (and vice-versa) if 
required. 


= The ability to make powerful key definitions that facilitate access ordered on combinations of up 
to eight fields. For example all records with key fields "Smith" and "Slough" can be found with 
"Smith" given the higher priority. 


= The opening of up to 31 index files for a DBF file at any one time. For example, a DBF file 
containing customer details can have two indexes, one for searching on customer occupation and 
related details and the other for searching on customer location and related details. 


= The adding, erasing, and updating of records with the appropriate updating of all associated 
index files carried out automatically thus greatly facilitating the task of maintaining an index. 


ISAM REFERENCE 


SSS SS ee ee ee er Ty 
DBF files 


Fields 
The following field types are available: 


BYTE, UBYTE, WORD, UWORD, LONG, ULONG, DOUBLE, STRING. 


However, if compatibility with existing OPL or PLIB DBF functions is required, the field types must be 
restricted to: 


WORD, LONG, DOUBLE, STRING 


Number of records 
The maximum number of records that can be handled by the PLIB DBF functions is 65534. 


However, in ISAM the maximum is 2147483647. 


SSS SSS a a a ee ea eee 
B-tree index files 


The ISAM library uses B-tree index files to provide much more powerful indexed and sequential access to 
DBF files than would otherwise be possible. 


In general B-tree index files should not be constructed or maintained on a Flash SSD. There is however 
no reason not to read an index file from Flash SSD. 


Block buffering 


B-tree index files are block structured files having a block size of 1SAM_BLOCK_SIZE (512) bytes (in the 
literature these blocks are sometimes referred to as pages - the terms are interchangeable). 


Index files are read using a least-recently-used (LRU) block buffering scheme - blocks that are repeatedly 
accessed are kept in memory (typically these are blocks near the root of the B-tree index). LRU block 
buffering has such an impact on the performance of the B-tree algorithm that it is not reasonable to do 
without it. 


The ISAM library implements a fixed-size LRU block pool per process regardless of the number of open 
index files. The size is fixed by calling the ISAM function o_1s_1Nn1T and specifying zero causes the 
default of 20 blocks (10K bytes) to be used. If more than one ISAM object is created, the same block 
pool is used; if a new size is specified in 0_!s_INIT, it is ignored if there are any index files still open. 
The new size is used once all of the original index files are closed. 


The LRU block pool is implemented in an external memory segment and does not, therefore, detract 
from the process data segment. The segment name will be BLKSnnnn.BLK where nnnn is the process ID in 
hex. 


Key description 


A key consists of up to eight fields with the constituent fields prioritised according to their order - the 
first having the highest priority - subject to an overall maximum key size of 64 bytes. For example, keys 
can be constructed from eight pous-e fields or, as a further example, the first 63 characters of a single 
text field (stored as a leading byte count string). 


The field types: BYTE, UBYTE, WORD, UWORD, LONG, ULONG and DOUBLE are compared numerically whereas 
STRING fields are compared lexically using a given number of characters at a given offset from the start. 
The comparison is subject to a programmer defined collation table. 


The specification of the key is stored in the header of the B-tree index file and the structure is defined by 
ISAM_KEYDEF: 


1 INTRODUCTION 


——___-__—————_-—j 


typedef struct 


€ 

WORD field; /* Field number for key */ 

UBYTE type; /* Type of field in key */ 

UBYTE flags; /* TSAM_FIELDFLAG_ASCEND or ISAM_FIELDFLAG_DESCEND */ 
UBYTE offset; /* Offset for start of comparison */ 

UBYTE len; /* Number of characters to compare */ 

WORD convert; /* Convert flag or collate table */ 


> ISAM_KEY_FIELD; 


typedef struct 
€ 
WORD nKeyFields; /* Number of key fields following... */ 
ISAM_KEY_FIELD keyField[ISAM_MAX_KEYFIELDS]; 
> ISAM_KEYDEF; 


The convert field can take one of the following values: 


ISAM_CONVERT_NOFOLD 
ISAM_CONVERT_FOLD 
ISAM_CONVERT_UPPER 
ISAM_CONVERT_LOWER 


which range from 0 - 3, or it can be the address of a user defined 256 byte collate table. 


The priority for the field comparisons is the order they are specified with the first one having highest 
priority. 
Size of B-tree files 


The size of a B-tree file depends upon the key size; therefore, it is good practice to keep the key as 
compact as possible. A worst-case B-tree file is only 50% full so the maximum size of a B-tree file is 
approximately: 


2*(number of records)*(key length + 6) 


For example, an index file with an eight byte key for a 1000 record file would require approximately 
28K bytes of storage. 


However, under typical use, a B-tree will be approximately 67% full and with the ISAM_MINIMISE flag set 
(see the o_Is_IFLAGs function) this is increased to approximately 86%. Note also that B-trees built from 
sorted data using 0_1S_1QBUILD or O_IS_IQADD are significantly more compact, typically 98% or over for 
large numbers of records. 


SSS SaaS Se eee eee eee eee ee 
Interface to the ISAM library 


The ISAM library is written using object-oriented programming (OOP). A class hierarchy has been 
implemented such that OOP programmers can subclass the library to handle record files other than DBF 
files and to use index files other than B-tree files. 


Most of the ISAM functions can be called using conventional C (using the PLIB p_send OF p_entersend 
functions) and the library is compatible with any combination of CLIB and PLIB libraries. 


The rest of this section describes how to use the ISAM library from conventional C. In any program 
using ISAM, the following steps will be involved: 


= Load the ISAM DYL. 

= Create an ISAM object 

= Call the ISAM functions 
= Destroy the ISAM object 


Loading the DYL 


The ISAM library is supplied as one file 1sam.pYL. Before any ISAM functions can be called, this must be 
loaded. This will get a category handle which is needed for an ISAM object to be created. The loading 
must be done in one of two ways depending on whether it has been linked into a multiple DYL file 
(normally an executable) or not. 


ISAM REFERENCE 


® If not linked to a multiple DYL file, the PLIB function p_toad! ib must be used, for example: 
HANDLE isamCat; 
p_loadl ib("ISAM.DYL", &isamCat, TRUE); 


will load 1sam.pyt from the current directory and write the category handle to isamCat. 


= If linked to a multiple DYL, the two PLIB functions p_opent ib and p_loadfilelib must be used, 
for example, if ISAM.DYL has been linked as the first DYL in an executable: 


HANDLE isamCat; 
VOID *chan; 


chan=NULL; 
if (p_openlib(&chan,DatCommandPtr)==0) 
p_loadfilelib(chan,0,&isamCat, TRUE); 
p_close(chan); 
will load IsaM.bYL from the current executable and write the category handle to isamCat. 


Creating an ISAM object 


Once the ISAM library has been loaded, an ISAM object can be created. The class of the object required 
is defined in isam.g as c_ptTbBF, which is an ISAM object using B-trees to access DBF files (ISAM may 
be subclassed to produce objects accessing other types of file). 


The PLIB functions p_newlibh or f_newl ibh can be used to create the object, for example: 


VOID *pIsam; 


plsam=p_newl ibh(isamCat,C_BIDBF); 


Since there is always some initialisation to be carried out, it is generally more convenient to use the PLIB 
function f_newlibhsend, for example: 


VOID *plsam; 


plsam=f_newl ibhsend( isamCat,C_BTDBF,O_IS_INIT,4096,0); 


will create an ISAM object and then call the function 0_1s_INIT to initialise a record buffer of 4096 bytes 
and the default index buffer (see the ISAM Functions chapter for the details of the 0_1s_1NIT function). 


Calling ISAM functions 


The ISAM functions are called by sending a message to an ISAM object using the PLIB functions p_send 
OI p_entersend. The messages are defined in isam.g and take the form 0_1s_xxx where xxx is the name of 
the function. Since all ISAM functions which can fail return zero for success or leave with a negative 
error number, p_entersend can easily be used to catch the error or p_send can be used if the error handling 
is done at a higher level. For example: 


INT c; 


if ((c=p_entersend4(plsam,O_IS_DOPEN, "TEST .DBF",P_FOPEN))<0) 
p_printfC"Open data file failed with error %d",c); 


catches the error whereas: 
p_send4(plsam,O_1S DOPEN, "TEST .DBF",P_FOPEN); 
passes any error to a higher level. 


Destroying the ISAM object 


The ISAM function 0_1s_DEsTROY will close the data file and all open index files and free all memory 
used and does not leave or return errors. However, since data is buffered, it could fail. Carefully written 
applications should therefore make sure that all data has been flushed and any error dealt with before 
calling 0_I1s_DEsTROY. The functions 0_1s_DFLUSH and 0_IS_IFLUSH must be used. 


Typical use 
There are many ISAM functions available; here is a sample in the order that they are typically used: 


= Create and initialise an ISAM object, use PLIB function f_newl ibhsend. 


1 INTRODUCTION 


SS eee 


= Set the field definition for the data file, use 0_1S_SET_FIELDDEF. 

= Open or create a data file, use 0_1S_DOPEN. 

= Set the key definition for an index file, use 0_1S_SET_KEYDEF. 

= Open or create one or more index files, use 0_1S_I0PEN. 

= Build an index file, use 0_Is_IBUILD. 

= Add or erase records, use 0_IS_ADD Or 0_IS_ERASE. 

= Access ordered data with keys, use 0_IS_IFIND Or 0_IS_INEXT etc. 
= Flush buffers, use o_1s_DFLUSH and 0_IS_IFLUSH. 

= Destroy the ISAM object, use 0_1S_DESTROY. 


Example programs 
Note that the following two examples do not include complete error handling. 


Building a complete (dense) index 


This program creates a DBF file called ExaMPLE.oBF with one field of type Lone and appends 100 random 
numbers to it. It then creates and builds an index file called Ex1.BTx of these numbers in ascending order. 


Note that in the argument list for the setFieldDef and setkeyDef subroutines, the TopSpeed C compiler 
understands the three dots to indicate an undefined number of arguments of undefined type. 


#include <p_std.h> 
#include <p_file.h> 
#include <p_sys.h> 


#include <epoc.h> 
#include <isam.g> 


GLREF_C TEXT *DatCommandPtr; 


LOCAL_C HANDLE isamCat; 
LOCAL_C VOID *plsam; 


LOCAL_C VOID pErrCUBYTE *mess, INT err) 
¢ 
UBYTE buf (E_MAX_ERROR_TEXT_SIZE]; 


p_errs(&buf [0] ,err); 

P_printf("%s failed - %s",mess, &buf [0] ); 
p_getch(); 

p_exit( TRUE); 

} 


LOCAL_C VOID loadIsam(VOID) 
€ 
INT c; 
TEXT dylname[P_FNAMESIZE]; 


p_fparse("ISAM.DYL", &dylname[0} ,NULL); 

if ((c=p_loadl ib(&dylname [0] , ,&isamCat, TRUE) )<0) 
pErr("Load ISAM.DYL",c); 

} 


LOCAL_C VOID createlsam(VOID) 
{ 


plsam=f_newl ibhsend(isamCat,C_BTDBF,O_IS_INIT,4096,0); 
> 


LOCAL_C VOID setFieldDef(INT nFields,...) 
{ 


p_send4(plIsam,0_IS_SET_FIELDDEF,nFields,&nFields+1); 


_- Oro 


ISAM REFERENCE 


LOCAL_C VOID setKeyDef(INT nKeyFields,...) 
€ 


p_send4(pIsam,O_IS_SET_KEYDEF ,nKeyFields, &nKeyFields+1); 
> 


LOCAL_C VOID openData(TEXT *name,UINT mode) 
€ 
INT c; 


if ((c=p_entersend4(pIsam,0_IS_DOPEN,name,mode))<0) 
pErr("Open data file",c); 
> 


LOCAL_C INT openIndex(TEXT *name,UINT mode) 
{ 
INT ¢; 


if ((c=p_entersend4(pIsam,O_IS_IOPEN,name,mode) )<0) 
pErr("Open index file",c); 

return(c); 

> 


GLDEF_C VOID main(VOID) 
{ 
ULONG seed; 
LONG lL; 
INT nRecords; 
INT indexid; 
INT i; 


loadIsam(); 

createlIsam(); 
setFieldDef(1,ISAM_FIELDTYPE_LONG); 

openData( "EXAMPLE .DBF",P_FREPLACE |P_FUPDATE); 
seed=0L; 

nRecords=100; 


p_printf("Generating %u records",nRecords); 
for (i=0;i<nRecords; i++) 
€ 
l=p_randl (&seed); 
p_sendS(pIsam,O_IS_PUT_FIELD,0,&t, ISAM_FIELDTYPE_LONG); 
p_send2(pIsam,0_IS_ADD); 
> 


p_printf ("Building index"); 

setKeyDef(1,0, 1SAM_FIELDTYPE_LONG, ISAM_FIELDFLAG_ASCEND); 
index Id=open!I ndex("EX1.BTX", P_FREPLACE |P_FUPDATE); 
p_send3(pIsam,O_IS_IBUILD, indexId); 


p_printf("Sorted data:"); 
p_send3(pIsam,O_IS_IFIRST, indexId); 
for (i=0;i<nRecords; i++) 
€ 
p_send5(pisam,O_IS GET_FIELD,0,&L,1SAM_FIELDTYPE_LONG); 
p_printf "4d", 1); 
p_send3(pIsam,OQ_IS_INEXT, indexId); 
> 
p_send2(pIsam,O_IS_ DESTROY); 
p_getch(); 
p_exit(0); 
> 


1 INTRODUCTION 
SS See 


Building a selective (sparse) index 


The following routine opens the DBF file called ExaMPLE DBF which was created in the previous example. 
It then creates and builds an index file called ex2.8Tx of those numbers which are divisible by ten in 
ascending order. 


LOCAL_C VOID divi0¢VOID) 
{ 
LONG Ll; 
INT indexId; 
INT eof; 


createlsam(); 
openData("EXAMPLE.DBF",P_FOPEN); 
p_printf("Build index of numbers divisible by 10"); 
setKeyDef(1,0, ISAM_FIELDTYPE_LONG, ISAM_FIELDFLAG_ ASCEND); 
indexId=openIndex("EX2.BTX", P_FREPLACE|P_FUPDATE); 
eof=p_send2(pIsam,O_IS_DFIRST); 
while (eof==FALSE) 
€ 
p_send5(pIsam,O_IS_GET_FIELD,0,&1,1SAM_FIELDTYPE_LONG); 
if (¢1%10)==0) 
p_send3(pisam,O_IS_IADD, indexId); 
eof=p_send2(plIsam,0_IS_DNEXT); 
> 
p_printf("Sorted data:"); 
eof=p_send3(plsam,0_IS_IFIRST, indexId); 
while (eof==FALSE) 
€ 
p_send5(pIsam,0_IS_GET_FIELD,0,&l,ISAM_FIELDTYPE_LONG); 
p_printt¢("%ld",L); 
eof=p_send3(pIsam,O_IS_INEXT, indexId); 
> 
p_send2(pIsam,0_IS_DESTROY); 
> 


CHAPTER 2 


ISAM FUNCTIONS 


ISAM functions are called using either p_send or p_entersend to the ISAM object created with (for 
example) the PLIB function p_newlibh. 


Most functions which can fail will return zero or a positive number for success or leave with a negative 
error number, giving the caller the option of catching the error using p_entersend or leaving the error 
handling to a higher level by calling p_send. 


Functions that can generate E_FILE_EoF (for example, 0_1S_INEXT), will return this negative error rather 
than leave. 


All functions can be called from standard C but o_1s_IFILTER and 0_1S_IpuP are only applicable when 
using object oriented programming (OOP). 


There are three types of functions: 
= General functions 
s Data file functions 
= Index file functions 


SSS a a a a ae eee 
General functions 
This section describes functions to perform the following: 

= Initialise and destroy an ISAM object. 

= Define and read the field definition for a data file. 

= Define and read the key definition for an index file. 

= Read and write data to the record buffer. 

® Set the format for conversion of numbers to text. 


= Add, erase and update records in the data file with appropriate updates to all associated index 
files. 


SINT eee 


VOID p_send4(VOID *pIsam, O_IS_INIT, INT rBufSize, INT iBufBlocks); 


Initialise the ISAM object with a record buffer of length raufLen and an index buffer in an external 
memory segment with isufBlocks blocks (a block is 1SAM_BLOCK_sIZE (512) bytes long). If iBufBlocks is 
zero, the default number of blocks, 8L_DEFAULT BLOCKS (20) is used. 


Performs the following initialisation: 
= Allocates rbufLen bytes for the record buffer. 
= Sets the field definition to the default of 32 string fields. 
= Sets the key definition to the default of the first eight characters of the first string field. 


ISAM REFERENCE 
TO 


= Sets the radix for number conversion to the default of 10. 


= Sets the format for doubles converted to text to the default of p_pTOB_GENERAL with a width of 20 
and a decimal point character of '.'. 


rBufLen sets the size of the record buffer and must be in the range 1SAM_MIN_RECORD_BUFFER (512) to 
ISAM_MAX_RECORD_BUFFER (16384). If it is not, p_panic will be called. The record buffer is used to read 
records from the data file and must be at least as long as the longest record to be read. A length of 
ISAM_MAX_RECORD_LENGTH+2 (4096) is guaranteed to be large enough for all records. When an index file is 
being built, data is read from the file in blocks as large as the record buffer; the bigger the buffer, the 
faster the build will be. 


iBufBlocks must be less than or equal to ISAM_MAX_SEG_BLOCKS (1024). Any value greater than this will 
cause p_panic to be called. 


Leaves with E_GEN_NOMEMoRY if there is insufficient memory to initialise everything. 


Typically, this function would be called when the object is created and can be combined into one call 
using (for example) the PLIB function f_newl ibhsend: 


plsam=f_newl ibhsend( isamCatNum, C_BTDBF,O_IS_INIT,4096,0); 


This will create an ISAM object and initialise it with a record buffer of 4096 bytes and the default 
number of index buffer blocks. 


Creating an ISAM object is explained further in the Introduction chapter. 


VOID p_send2(VOID *pIsam, O_IS_DESTROY); 
Destroy the ISAM object. The data file and all open index files are closed and all memory is freed. 


Note that if more than one ISAM object is created in the same process, the external segment memory 
used for buffering the indexes is shared and will only be freed when the last ISAM object is destroyed. 


O_IS_DESTROY can fail due to one or more write operations but will always succeed in destroying the 
ISAM object (freeing all memory used). No error is ever returned and the function will not leave. 


Carefully written applications avoid this problem by using o_1s_pFLUSH and 0_IS_IFLUSH, to flush all 
buffered data (and taking appropriate action if this fails) before destroying the object without risk of 
failure. 


INT p_send4(VOID *pisam, O_IS_SET_FIELDDEF, INT nFields, VOID *pArgs); 


Set the field definition to contain nFietds with types in the list at pargs. This field definition will be used 
when a data file is created. 


Alternatively pargs can point to an ISAM_FIELDDEF structure containing the field definition while nfields is 
passed as zero. 


The ISAM_FIELDDEF structure is defined in isam.g as: 


typedef struct 
€ 
WORD nFields; 
UBYTE type[ISAM_MAX_DEFINED_FIELDS] ; 
> ISAM_FIELDDEF 


For example: 


LOCAL_C VOID defineFields(VOID) 
€ 
ISAM_FIELDDEF fieldDef; 


fieldDef .nFields=2; 
fieldDef.type[0]=ISAM_FIELDTYPE_WORD; 
fieldDef.typel1]=ISAM_FIELDTYPE_LONG; 
p_send4(pisam,0_IS_SET_FIELDDEF,0,&fieldDef); 
> 


SS ee SS ee 
10 


2 ISAM FUNCTIONS 
—_ See 


is equivalent to: 


LOCAL_C VOID setFieldDef(INT nFields,...) 
€ 


p_send4(pIsam,O_IS_SET_FIELDDEF ,nFields,&nFields+1)); 
> 


LOCAL_C VOID defineFields(VOID) 
€ 


setFieldDef(2,1SAM_FIELDTYPE_WORD, ISAM_FIELDTYPE_LONG); 
> 


and both define the field structure of the data file to be two fields, the first a woRD field, the second a 
LONG. 


The maximum number of fields that can be specified is 1SAM_MAX_DEFINED_FIELDS (32) and each field type 
must be one of the following types: 


ISAM_FIELDTYPE_BYTE for a signed Byte field. 
ISAM_FIELDTYPE_UBYTE for an unsigned usyTE field. 
ISAM_FIELDTYPE_WORD for a signed worp field. 
ISAM_FIELDTYPE_UWORD for an unsigned uworp field. 
ISAM_FIELDTYPE_LONG for a signed tone field. 
ISAM_FIELDTYPE_ULONG for an unsigned uLonc field. 
ISAM_FIELDTYPE_DOUBLE for a DOUBLE field. 
ISAM_FIELDTYPE_STRING for a leading byte count string field. 


More fields than are specified in the field definition can be accessed, but they are always assumed to be 
of type ISAM_FIELDTYPE_STRING. 


Note that this specifies the field types to be used in the actual data file; however, conversion to and from 
I1SAM_FIELDTYPE_STRING is performed automatically by the functions 0_1S_PUT_FIELD and O_IS_GET_FIELD. 
See the descriptions of these two functions for further details. 


Similarly, index files may specify field types which differ from those specified for data file fields. The 
field types will be converted automatically, provided the conversion is to or from ISAM_FIELDTYPE_STRING. 
See the description of 0_1$_SET_KEYDEF. 


Retums zero if successful or leaves with &_GEN_ARG if the given field definition is invalid. 


ISAM_FIELDDEF *p_send2(VOID *pIsam, O_IS GET_FIELDDEF); 


Get the current field definition, returning a pointer to an 1SAM_FIELDDEF structure. 


INT p_send4(VOID *plsam, O_IS_SET_KEYDEF, INT nKeyFields, VOID *pArgs); 


Set the key definition (to be used for the next created index file) to contain nkeyFields with the 
parameters for each key field specified in the list at pArgs. This key definition will be used for the next 
index file created. 


Alternatively pArgs can point to an ISAM_KEYDEF structure containing the key definition while nFields is 
passed as zero. 


The 1SAM_KEYDEF structure is defined in isam.g by: 


11 


ISAM REFERENCE 


typedef struct 


€ 

WORD field; /* Field number for key */ 

UBYTE type; /* Type of field in key */ 

UBYTE flags; /* ISAM_FIELDFLAG_ASCEND or ISAM_FIELDTYPE_DESCEND */ 
UBYTE offset; /* Offset for string comparison */ 

UBYTE len: /* Length for string comparison */ 

WORD convert; /* Convert flag or collate table */ 


} ISAM_KEY_FIELD; 


typedef struct 


€ 

WORD nkKeyFields; 

ISAM_KEY_FIELD keyField[ISAM_MAX_KEYFIELDS]; 
} ISAM_KEYDEF; 


For example: 


LOCAL_C VOID defineKey(VOID) 


€ 
ISAM_KEYDEF keyDef; 


keyDef .nKeyFields=2; 

keyDef .keyField[0] .field=3; 

keyDef .keyField[0] . type=ISAM_FIELDTYPE_STRING; 
keyDef .keyField[0] . flags=1SAM_FIELDFLAG_ASCEND; 
keyDef .keyField[0] .offset=0; 

keyDef .keyField[0] . Len=8; 

keyDef .keyField[0] .convert=1SAM_CONVERT_NOFOLD; 
keyDef .keyField[1] .field=1; 
keyDef.keyField[1] . type=ISAM_FIELDTYPE_LONG; 
keyDef .keyField[1] .flags=1SAM_FIELDFLAG_ASCEND; 
p_send4(pIsam,O_IS_SET_KEYDEF ,0,&keyDef); 

3 


is equivalent to: 


LOCAL_C VOID setKeyDefCINT nKeyFields,...) 


{€ 


p_send4(pIsam,0_IS_SET_KEYDEF ,nKeyFields, &nKeyFields+1); 
> 


LOCAL_C VOID defineKey(VOID) 


€ 


setKeyDef(2,3, 1SAM_FIELOTYPE_STRING, ISAM_FIELDFLAG_ASCEND,0,8,1SAM_CONVERT_NOFOLD, 1, 


TSAM_FIELDTYPE_LONG, ISAM_FIELDTYPE_ASCEND); 


> 


The fields: offset, len and convert must only be supplied if the type is 1SAM_FIELDTYPE_STRING. 


The key definition is subject to the following validation: 


The number of key fields must be in the range 1 to 1SAM_MAX_KEYFIELDS (8). 
The field number for each key field must be in the range 0 to 1SAM_MAX_RECORD_FIELDS (4094). 


The type of each key field (the type actually stored in the index file) must be valid and must be 
either the same as the type specified in the current field definition or be a conversion from or to 
an ISAM_FIELDTYPE_STRING, 


The length of a key produced by this definition must be no greater than ISAM_MAX_KEY_LENGTH 
(64). 


Returns the length of a key resulting from this definition if successful or leaves with €_GEN_aRG if the key 
definition is invalid. 


12 


2 ISAM FUNCTIONS 


ISAM_KEYDEF “p_send3(VOID *plsam, O_IS_GET_KEYDEF, INT indexId); 


Get the key definition for the index indexid, returning a pointer to an 1SAM_KEYDEF structure. The value 
indexid is returned when an index is opened (see 0_1S_I10PEN below). 


INT p_send5(VOID “plsam, O_IS_PUT_FIELD, INT field, VOID *pData, INT type); 


Put the data at address pata into the record buffer at field number given by field. If any preceding fields 
have not been set, they will be automatically set to contain zeros. 


The type parameter is the type of the data pointed to by pdata. If type is ISAM_FIELDTYPE_STRING, the data 
at pData must be in the form of a leading byte string. 


If type is different from that specified for field number field (as reported by 0_IS_GET_FIELDDEF) and 
either of these two types is ISAM_FIELDTYPE_STRING, the field data will be converted between numeric and 
string types as necessary according to the current radix (as set by 0_IS_SET_RADIX). 


For example, if the field definition is set to 32 strings and the current radix is decimal (the defaults), the 
following code will put the value "31" into field zero in the record buffer as a leading byte count string. 


WORD value; 


value=31; 
p_send5(pIsam,0_IS_PUT_FIELD,0,&value, ISAM_FIELDTYPE_WORD); 


Returns zero if successful or leaves with one of the following negative error numbers: 


E_FILE_RECORD the assignment to the field would make the total record length greater than 
ISAM_MAX_RECORD_LENGTH (4094) or the size of the buffer specified in 0_1S_INIT. 

E_GEN_ARG the type conversion required is not to or from ISAM_FIELDTYPE_STRING. 

E_GEN_FAIL the type conversion failed because the string could not be recognised as a 
number. 

E_GEN_OVER the type conversion failed because the number is out of range. 


INT p_sendS(VOID *pisam, O_IS_GET FIELD, INT field, VOID *pData, INT t e@); 
- eT YP! 


Get the data from field in the record buffer into the buffer at poata. 


The type parameter is the type of the data required at ppata. If type is ISAM_FIELDTYPE_STRING, the data 
will be written to poata in the form of a leading byte string. 


If type is different from that specified for field number field (as reported by 0_IS_GET_FIELDDEF) and 
either of these two types is 1SAM_FIELDTYPE_STRING, the field data will be converted between numeric and 
string types as necessary according to the current radix (as set by 0_IS_SET_RADIX). 


For example, if the field definition is set to 32 strings (the default), the following code will get the value 
from field 10 in the record buffer and convert it to a LONG. 


LONG lValue; 


p_send5(pIsam,O_IS_GET_FIELD,10,&lValue, ISAM_FIELDTYPE_LONG); 


Retums zero if successful or leaves with one of the following negative error numbers: 


E_GEN_ARG the type conversion required is not to or from 1SAM_FIELDTYPE_STRING. 

E_GEN_FAIL the type conversion failed because the string could not be recognised as a 
number. 

£_GEN_OVER the type conversion failed because the number is out of range. 


13 


INT p_send3(VOID *pIsam, O_IS_GET_TYPE, INT field); 
Return the actual type of field in the data file. 


This provides the same functionality as calling 0_1s_GET_FIELDDEF and then inspecting the required field. 
It is included for convenience. 


ISAM_RECORDBUF *p_send2(VOID *plsam, 0_IS_GET_RBUF); 


Get the actual address of the record buffer allocated in 0_1s_1NIT, returning a pointer to an ISAM_RECORDBUF 
structure. This struct is not defined in any header file - users of this method function should declare the 
struct in their own code as: 


typedef struct 
{ 
WORD Len; /* Length of record */ 
UBYTE data[2]; /* len bytes of data... */ 


} ISAM_RECORDBUF; 


This function is provided to allow direct access to the current record in the buffer, if needed. Typically, 
it will be more convenient to use 0_IS_GET_FIELD and ©_IS_PUT_FIELD. 


VOID p_send3(VOID “pIsam, C_IS_SET_RADIX, INT radix); 
Set the base radix for number conversion to radix. By default this is set to 10. 


VOID p_send3(VOID *pIsam, O_IS_SET_DFORMAT, P_DTOB *dFormat); 
Set the format for pousLEs converted to text to be that specified by dFormat. 


INT p_send2(VOID *pIsam, O_IS_ADD); 


Add the record currently in the record buffer to the data file and attempt to add entries to all open index 
files which do not have 1SAM_FLAG_MANUAL set (see 0_IS_IFLAGS). 


One or more of the index files may refuse to actually add the entry for any of the following reasons: 
= A filter method returns FALSE (see 0_1S_IFILTER). 


= The entry matches an existing entry in the index and a duplicate method returns FALSE (see 
O_IS_IDUP). 


" The entry matches an existing entry in the index and I1SAM_FLAG_ALLOWDUP is not set (see 
O_IS_IFLAGS). 


but no error will be given. 


Returns FALSE if successful or leaves with one of the negative errors returned by the PLIB function 
p_write. 


s 


INT p_send2(VOID *pIsam, O_IS_ ERASE); 


Erase the record currently in the record buffer from the data file and from all open index files which do 
not have ISAM_FLAG_MANUAL set (see O_IS_IFLAGS). 


The record buffer must contain a record read by a successful call to one of the following functions: 
O_IS_DFIRST, O_1S_DNEXT, O_IS_IFIND, O_IS_INEXT, O_IS_IBACK, O_IS_IFIRST, O_IS_ILAST, O_IS_ICURRENT. 


SSS SS Se ee eee 
14 


2 ISAM FUNCTIONS 
—. EES 


Retums FALSE if successful or €_FILE_EoF if the current record is end-of-file or leaves with one of the 
negative errors retumed by the PLIB function p write. 


INT p_send2(VOID *pisam, O_IS_ UPDATE); 


Replace the record which was read by a successful call to one of the following functions: 0_1s_DFIRST, 
O_IS_DNEXT, O_IS_IFIND, O_IS_INEXT, O_IS_IBACK, O_IS_IFIRST, O_IS_ILAST, O_IS_ICURRENT, by the new 
contents of the record buffer in the data file and all open index files which do not have 1SAM_FLAG_MANUAL 
set (see O_IS_IFLAGS). 


Returns FALSE if successful or €_FILE_Eor if the current record is end-of-file or leaves with one of the 
negative errors returned by the PLIB function p_write. 


SS ea en ee eS] 
Data file functions 
This section describes functions to perform the following: 


= Open or create a data file. 

= Flush the buffers used for the data file. 
= Get the current size of the data file. 

=  Sequentially read the data file. 


INT p_send4(VOID *plsam, O_IS_DOPEN, TEXT *name, UINT mode); 
Open a data file specified by the zero terminated file specification name. 


The mode parameter is the same as for the PLIB function p_open(P_FSTREAM). If P_FCREATE OF P_FREPLACE is 
specified, the data file is created with the current field definition (see 0_1S_SET_FIELDDEF). If an existing 
data file is opened, the current field definition is set to that of the file opened. 


Only one data file can be opened per ISAM object. If an attempt is made to open more than one, P_panic 
will be called. 


Retums zero if successful or leaves with one of the negative errors returned by p_open(P_FSTREAM). 


INT p_send2(VOID *pisam, O_IS_DFLUSH); 
Flush al! data written to the data file and write the file's modification date. 


Returns zero if successful or leaves with one of the negative errors returned by the PLIB function 
P_write. 


See the PLIB function p_iow(P_FFLUSK) for further details on flushing. 


Sra 


INT p_send3(VOID *pIsam, O_IS DSIZE, LONG *size); 
Write the size (in bytes) of the open data file to size. 


Retums zero if successful or leaves with one of the negative errors returned by the PLIB function p_seek. 


INT p_send2(VOID *plsam, O_IS_DFIRST); 
Read the first record in the data file into the record buffer. 


15 


ISAM REFERENCE 


Returns zero if successful or £_FILE_EoF if there are no records or leaves with one of the negative errors 
returned by the PLIB function p_read. 


Read the next record in the data file into the record buffer. 


Should be used in conjunctions with o_1s_DFIRST to provide sequential access to the data file records in 
the order they are stored without the need for an index file. 


Returns zero if successful or E_FILE_EOF if the current record is the last record in the file or leaves with 
one of the negative errors returned by the PLIB function p_read. 


a a a a a a ed 
Index file functions 
This section describes functions to perform the following: 
= Open or create an index file. 
= Flush the buffers used for the index file. 
® Build a complete (dense) index file from a sorted or unsorted data file. 
= Build an index for a selection of records (sparse) by one of two mechanisms: 
1) Specify a function which will be called during the building of the index (OOP only). 
2) Add and erase entries from the index directly. 
= Get the size and number of entries in an index. 
= Provide fast indexed access to records in the data file from a given index and match key. 
= Provide sequential access to records in the data file in the order defined by an index. 


The function 0_I1S_IOPEN returns an indexId which is then passed as a parameter to other index functions. 


INT p_send4(VOID *pIsam, O_IS_IOPEN, TEXT “name, UINT mode); 


Open an index file specified by the zero terminated file specification name. 


The mode parameter has the same meaning as for the PLIB function p_opencP_FSTREAM), except that 
P_FAPPEND should not be specified. If P_FCREATE or P_FREPLACE is specified, the data file is created with the 
current key definition (see 0_1S_SET_KEYDEF above). If an existing index file is opened (with a mode of 
P_FOPEN, optionally ored with P_FUPDATE) its key definition can be obtaining using 0_1s_GET_KEYDEF. 


Note that the key definition is stored in the index file itself and cannot be changed once the index file has 
been created. 


Retums an indexid from 1 to 31 if successful or leaves with one of the negative errors returned by 
P_open(P_FSTREAM) Or E_GEN_FAIL if an attempt is made to open more than 31 indexes. 


INT p_send3(VOID *plsam, O_IS_ICLOSE, INT indexId); 
Close the index specified by indexid and return zero. 


0_IS_ICLOSE can fail due to one or more write operations but will always succeed in closing the channel 
(the indexid should not be used subsequently). No error is ever returned and the function will not leave. 


Carefully written applications avoid this problem by using 0_!s_IFLUSH to flush all buffered data (and 
taking appropriate action it this fails) before closing the channel without risk of failure. 


16 


2 ISAM FUNCTIONS 


INT p_send3(VOID *plsam, O_IS_IFLUSH, INT indexld); 
Flush all buffers used for index indextd and write the index file's modification date. 


Returns zero if successful or leaves with one of the negative errors returned by the PLIB function 
p_write. 


See the PLIB function p_iow¢P_FFLUSH) for further details on flushing. 


INT p_sendS(VOID *plsam, O_IS_IFLAGS, INT indexId, INT mask, INT value); 


Modify the flag bits given by mask to the state (set or clear) given by value in index indextd. 
Returns the new value of the flags; passing mask as Zero just reads the current flag settings. 
The flags available are: 


ISAM_FLAG_ALLOWDUP allow duplicate entries in the index. If this flag is set, duplicate entries will be 
added when the index is built using 0_I1s_BUILD or 0_IS_QBUILD or when entries 
are added with 0_1S_ADD or 0_IS_IADD. 


1SAM_FLAG_MANUAL the index will not be automatically updated by calls to the general functions 
0_1S_ADD, O_IS_ERASE or 0_IS_UPDATE. The index functions 0_1S_1ADD and 
O_IS_IERASE must be used to add or erase entries explicitly; typically, it is used 
to build an index to a selection of records only (a sparse index). 


ISAM_FLAG_MINIMIZE Minimize the storage requirement of the index by altering the way keys are 
inserted. The index is not compressed when the flag is set but any future 
additions to the index are inserted using the minimize algorithm. With the flag 
set, the actual insertion will be slower but the index produced will be smaller 
and hence a greater portion of it can be held in internal buffers which means 
finding the insertion point is faster. 


Whether it is worth setting this flag is clearly application dependent and the 
best way to decide is by trial and error. 


The flag can be set or cleared at any stage without harming the index structure. 
By default, all the above flags are not set. 


Note that these settings are stored in the index file but can be modified at any time. For example, if 
1SAM_FLAG_ALLOWDUP is not set, it does not necessarily mean there are no duplicate entries in the index. It 
just means that the current intention is to not add duplicates. 


Example 


testId=p_send4(pIsam,0_IS_IOPEN, "TEST. INX", P_FREPLACE|P_FUPDATE); 
p_send5(pIsam,O_IS_IFLAGS, testId, ISAM_FLAG_ALLOWDUP, ISAM_FLAG_ALLOWDUP); 


creates an index file in which duplicate entries will be allowed. 


_ Specify a filter method 
VOID p_sendS(VOID *pIsam, O_IS_IFILTER, INT indexid, VOID *pObj, INT method); 
This function is only applicable when using object oriented programming (OOP). 


Specify the object at p0bj to be called with method whenever entries are added to index indextd, to 
facilitate the building of an index to a selection of records (a sparse index). 


The method is called with a pointer to the key that is being inserted and the address of a LoNG which is the 
data file reference. The method must return FALSE if the entry should be omitted from the index or TRUE if 
it should be added. 


To remove the filter method, pobj must be passed as NULL. 


17 


ISAM REFERENCE 


pil 
VOID p_send5(VOID *plsam, O_IS_IDUP, INT indexId, VOID *pObj, INT method); 

This function is only applicable when using object oriented programming (OOP). 
Specify the object at pobj to be called with method whenever a duplicate entry is added to index indexId. 


The method is called with a pointer to the key that is being inserted and the address of a Lonc which is the 
data file reference. The method must return FALSE if the duplicate entry should be omitted from the index 
or TRUE if it should be added anyway. There is no regard for the setting of the flag 1SAM_FLAG_ALLOWDUP in 
either case, ie the method called has higher priority. 


To remove the duplicates method, pobj must be passed as NULL. 


INT p_send3(VOID *plsam, O_IS_IBUILD, INT indexId); 


Build the index specified by indexid using its key definition to extract keys from the records in the data 
file. 


If the data file is already ordered by the key for this index, 0_1s_1aBuILD should be used to build the 
index much faster. 


The index is always built from scratch; any existing index entries are erased first. 


Returns FALSE if the complete build was successful and there were no duplicate entries; returns TRUE if 
successful but there were duplicates or leaves with one of the negative errors returned by the PLIB 
function p_write in which case all entries made so far will be erased. 


Duplicate entries 


The value TRUE, returned by 0_1$_IBUILD is simply to inform the caller that one or more keys extracted 
from the data during the build were identical. It does not, however, indicate whether more than one copy 
of the key has been stored in the index or not. This is controlled by the following: 


= Ifa duplicates method has been specified by 0_1s_1pup, its return value of TRUE or FALSE 
determines whether to insert or not. (This is applicable to OOP only.) 


= If there is no duplicates method, the setting of the flag 1SAM_FLAG_ALLOWDUP is used. 


INT p_send3(VOID *plIsam, O_IS_IQBUILD, INT indexId); 


Build the index specified by indexid using its key definition to extract keys from the records in the 
(sorted) data file. 


For this function to work, the records in the data file must be in the correct order defined by the key 
definition for this index. 


There are two advantages of this function over 0_Is_1BUILD: 


1) It is much faster for a large number of records. So much faster, in fact, that it is often quicker to 
sort the data file (using, for example, a quick-sort algorithm) and then call 0_1s_1aBuILb to build 
an index than to call 0_1s_1BUILD with an unsorted data file. 


2) The index file produced will generally be smaller (more compressed) than that produced using 
O_1S_IBUILD. Note that if the flag ISAM_FLAG_MINIMIZE is set before the call to 0_IS_QBUILD, an 
even smaller index file will be produced at a small cost to the time taken (see the 0_1S_IFLAGS 
function, above). 


The index is always built from scratch, so any existing index entries are erased first. 


Returns FALSE if the complete build is successful or leaves with one of the negative errors returned by the 
PLIB function p write in which case all entries made so far will be erased. 


Note that there is no checking for duplicate entries as there is with the o_1s_1BUILD function. 


18 


2 ISAM FUNCTIONS 


INT p_send3(VOID *plsam, O_IS_IADD, INT indexId); 


Add an entry to the index indexid using its key definition to extract a key from the record currently in the 
record buffer. 


The entry is added to the given index only, unlike the function 0_1s_app which adds entries to all 
appropriate indexes automatically. This provides, therefore, the mechanism for building and maintaining 
a selective (sparse) index: 


= Read records into the record buffer using, for example, 0_1S_DNEXT, Or O_IS_IFIND, 0_IS_INEXT 
with another index. 


= Selectively add entries to this index if certain conditions are met. 


This method of building an index will typically be slower than building a sparse index by calling 
O_IS_IBUILD Or 0_IS_IQBUILD after specifying a filter method but does not require OOP. 


If the entries are to be added in key order, the function 0_1s_1aapp should be used to add the entries 
faster. 


Retumis FALSE if successful or TRUE if successful but the entry is a duplicate or leaves with one of the 
negative errors returned by the PLIB function p_write. 


The decision whether to add a duplicate entry is made in exactly the same way as with 0_IS_IBUILD. 


INT p_send4(VOID *pIsam, O_IS_IQADD, INT indexId, INT finish); 


Add an entry to the index indexid in order using its key definition to extract a key from the record 
currently in the record buffer. 


The entries must be added in the correct order given by the key for this index and the parameter finish 
should be passed as FALsE for all entries except the last entry, when it should be passed as TRUE. 


The index should contain no entries before the first 0_1s_1aapp is called. 
The advantages of this function over 0_1s_1apD are the same as 0_IS_IQBUILD over O_IS_IBUILD. 


Returms FALSE if successful or leaves with one of the negative errors returned by the PLIB function 
P_write. 


Note that there is no checking for duplicate entries as there is with the 0_1s_1app function. 


1S_IEF 


INT p_send3(VOID *pIsam, O_IS_IERASE, INT indexId); 


Erase an entry from the index indexid, which corresponds to the record currently in the record buffer. 


The entry is erased from the given index only, unlike the function 0_1s_ERASE which erases entries from 
all appropriate indexes automatically. This provides, therefore, the mechanism for maintaining a selective 
(sparse) index. 


Returns FALSE if successful or TRUE if there is no entry in the index which corresponds to the record in the 
record buffer or leaves with one of the negative errors returned by the PLIB function p_write. 


INT p_send3(VOID *pisam, O_IS_IERASEALL, INT indexId); 


Erase all entries in the index indextd. 


Returns zero if successful or leaves with one of the negative errors returned by the PLIB function 
p_write. 


19 


INT p_send4(VOID *pIsam, O_IS_ICOUNT, INT indexId, LONG *count); 
Write the number of entries in the open index file indexId to count. 


Returns zero if successful or leaves with one of the negative errors returned by the PLIB function p_seek. 


INT p_send4(VOID *pIsam, O_IS_DSIZE, INT indexId, LONG *size); 


Write the size (in bytes) of the open index file indexid to size. 


Returns zero if successful or leaves with one of the negative errors returned by the PLIB function p_seek. 


INT p_send4(VOID *pisam, O_IS_IFIND, INT indexId, VOID **pArgs); 


Find an entry in the index indexid which matches the key generated by the arguments at pargs and read 
the corresponding record from the data file into the record buffer. 


pArgs is the address of an array of pointers to key fields to be matched, terminated by NULL if fewer key 
fields are supplied than are specified in the key definition for this index. 


Returns the following: 

FALSE if a matching entry is found and the corresponding record is read into the 
record buffer. 

TRUE if no match is found and the record corresponding to the following index entry 
is read into the buffer. 

E_FILE_EOF if no match is found and there is no following index entry (no record is read 


into the record buffer). 
or leaves with a negative error returned from the PLIB function p_read. 
For example: 


LOCAL_C INT findEntryCINT indexId,...) 
{ 


return(p_send4(pisam,O_IS_IFIND, indexId, &indexId+1)); 
> 


LOCAL_C VOID find(VoID) 
{ 
UBYTE str[16]; 
LONG lL; 


L=99999L ; 

str [0J=3; 

str[ij="A'; 

str[2]="B'; 

str(3]='C'; 

if (findEntry(index1,&l,&str [0] ,NULL)==FALSE) 
p_printf ("Found") ; 

else 
p_printf("Not found"); 

> 


will search index’ for the first entry with field zero equal to 999991 and field one equal to asc. Note that 
the field types passed must be the same as those specified in the key definition of the index. 


If there is more than one entry in the index which matches the given fields, 0_1s_IFIND will always read 
the first one. To read any others, use 0_IS_INEXT. 


20 


2 ISAM FUNCTIONS 


INT p_send3(VOID *pIsam, O_IS_INEXT, INT indexId); 
Read the record corresponding to the next entry in index indexid into the record buffer. 


Returns zero if successful or £_FILE_EoF if the current index entry is the last one or leaves with one of the 
negative errors returned by the PLIB function p_read. 


If two or more indexes are open at the same time, this method function may return E_FILE_EOF 
erroneously. If you use two or more indexes simultaneously, you should access this method via a utility 
function of the following form: 


GLDEF_C INT DoInext(VOID *pIsam, INT indexId) 
€ 
INT ret; 


ret=p_send3(pIsam,O_IS_INEXT, indexId); 

if (ret==E_FILE_EOF) 
{ 
p_send3(pIsam,0_IS_IBACK, indexId); 
p_send3(pIsam,O_IS_INEXT, indexId); 
ret=p_send3(pIsam,O_IS_INEXT, indexId); 
> 

return(ret); 

> 


INT p_send3(VOID *plsam, O_IS_IBACK, INT indexId); 


Read the record corresponding to the next entry in index index!d into the record buffer. 


Retums zero if successful or €_FILE_E0F if the current index entry is the first one or leaves with one of the 
negative errors returned by the PLIB function p_read. 


INT p_send3(VOID *pIsam, O_IS_IFIRST, INT indexId); 
Read the record corresponding to the first entry in index indexid into the record buffer. 


Returns zero if successful or €_F1L€_EOF if there are no entries in the index or leaves with one of the 
negative errors returned by the PLIB function p_read. 


INT p_send3(VOID *pIsam, O_IS_ILAST, INT indexId); 


Read the record corresponding to the last entry in index indexid into the record buffer. 


Returns zero if successful or €_F1LE_E0F if there are no entries in the index or leaves with one of the 
negative errors returned by the PLIB function p_read. 


INT p_send3(VOID *pIsam, O_IS_ICURRENT, INT indexId); 


Read the record corresponding to the current entry in index indexid into the record buffer. 


Retums zero if successful or €_F1LE€_E0F if the current index entry is set to end-of-file or leaves with one 
of the negative errors returned by the PLIB function p_read. 


21 


INDEX 


IS ADD 14 
-18 DESTROY 10 
DFIRST 15 
“DFLUSH 15 


ERASE 14 
GET FIELD 13 
GET_FIELDDEF 11 
GET_KEYDEF 13 
GET_RBUF 14 
GET_TYPE 14 
“IADD 19 
“IBACK 21 
IBUILD 18 
ICLOSE 16 
“ICOUNT 20 
“ICURRENT 21 
“IDUP 18 
IERASE 19 
“IERASEALL 19 
TIFILTER 17 
“IFIND 20 
IFIRST 21 
IFLAGS 17 
“IFLUSH 17 
ILAST 21 
“INEXT 21 

INIT 9 

“IOPEN 16 
“IQADD 19 
“IOBUILD 18 
“ISIZE 20 

“PUT FIELD 13 
"SET_DFORMAT 14 
SET_FIELDDEF 10 
~SET_KEYDEF 11 
"SET_RADIX 14 
"UPDATE 15 


1111919919191 9, 92,9, 9,2,2,9,9,9,9,2,9,9,9,0,9,0.0 


FIAVAIIRAA AAD ADD BADAADARARA DADO RBBB AAD D 


