|
RXDbase: A NetRexx database class - @ Max Marsiglietti
1996,97,98.
What is RXDbase.
My
goal is to provide a simple yet efficient database class which has
the following features:
Use of the RXDBase class
import nrio.RXDbase
Is required in your NetRexx source
file.
A detailed explanation of all the methods.
Why
did I state 'methods', and not also 'properties' or 'exceptions'
? Simply put, the RXDbase class has no public properties, and
doesn't throw exceptions. Instead, it has methods to find out if
there have been errors, and to query the value of some properties.
Limitations:
The default return strings for the
following methods are: DBNOTSTARTED:
(can't work on nothing) READY: (no
errors) SYNTAX ERROR: (go
figure) ERROR:FILE ACCESS PROBLEMS
(something weird at file level)
Unless stated otherwise.
More error codes will be introduced as the RXDbase will be
enhanced.
Index of available constructors and methods:
RXDbase()
- A simple constructor.
RXDbase(Rexx rName, Rexx[,] rRec)
- A constructor used to create a database.
RXDbase(Rexx rName)
- A constructor used to connect to an existing database.
Rexx addfield(Rexx rFieldName, Rexx
rFieldLength, Rexx rFieldIndexingMethod)
- Adds a field to the currently opened database.
Rexx connect(Rexx rName, Rexx[,] rRec)
- Creates a new database and connects to it.
Rexx connect(Rexx rName)
- Connects to an existing database.
Rexx definefilter(Rexx rFilter)
- Defines a filter to be used in future loadrec calls.
Rexx delerec(Rexx[] rToDele, boolean
bDelete)
- Deletes/Undeletes one or more records.
Rexx deletedb(Rexx rDbName)
- Deletes a whole database.
Rexx delfield(Rexx rFieldName)
- Deletes a field of the currently opened database.
Rexx disconnect()
- Disconnects from the currently opened database.
Rexx[, ] getdbinfo()
- Gets information about record structure for the currently
opened database.
Rexx[] getdbstats()
- Gets some useful statistics about the database.
Rexx[] getdbstatus()
- Returns the current in use filter and release version for
RXDbase.
Rexx globalrebuildidx()
- Rebuilds all of the indexes.
Rexx[, ] loadrec(Rexx rIdx, Rexx
rHowManyRec, Rexx rStart, boolean bForward)
- Loads a set of records.
Rexx[, ] loadrecwithfilter(Rexx
rIdx, Rexx rHowManyRec, Rexx rStart, boolean bForward, Rexx
rFilter)
- Loads a set of records according to a temporary filter.
Rexx modfieldidx(Rexx rFieldName, Rexx
rFieldIndexingMethod)
- Modifies (on the fly) indexing strategy for a field.
Rexx modfieldlength(Rexx rFieldName,
Rexx rNewLength)
- Modifies (on the fly) the length of a field.
Rexx modfieldname(Rexx rOldName, Rexx
rNewName)
- Modifies (on the fly) the name of a field.
Rexx modirec(Rexx[, ] rToModi)
- Modifies one or more records.
Rexx pack()
- Packs the database (physically purge deleted records).
Rexx rebuildidx(Rexx rFieldName)
- Rebuilds the index for a single field.
Rexx saverec(Rexx[, ] rToSave, Rexx rMode)
- Stores one or more records in the database.
Rexx status()
- Returns the current error
status of the database.
The following constructors are
available for the RXDbase class:
RXDbase() RXDbase(Rexx
rName, Rexx[, ] rRec) RXDbase(Rexx
rName)
The above two constructors works as in the CONNECT
method (see below).
The following methods can be
issued on the RXDbase class:
Rexx
addfield(Rexx rFieldName, Rexx rFieldLength, Rexx
rFieldIndexingMethod)
With this method you can add a field to a database;
you can also specify an index, which will be built automatically
for you.
Notes: Don't use names already in use in the same
database, and specify a valid indexing method (currently
supported: "ISAMxx" and "NONEx"); The new
fields' contents will be blank for all records (of course). See
the "connect" method for a listing of valid XX, X
values.
Rexx
connect(Rexx rName, Rexx[, ] rRec) This method is the
very first to issue, if you have not instantiated an RXDbase
variable with the two-parameters or one-parameter constructor. If
you have, you can omit this method.
rName is the name of
the file without any extension that is hosting/will host
the database on the disk. RXDbase will add the file name extension
'.dat' to this name.
rRec is a
bi-dimensional Rexx array that has to be built this way: rRec[0,
0] --> Number of fields in the database. rRec[n, 0] -->
Length (a whole number stored in a Rexx variable) of field
'n'. rRec[n, 1] --> Name of field 'n' (it will be used
later). rRec[n, 2] --> Indexing method of the field: it can
be any of the following: ISAM = Indexed Sequential Access
Method, good if you need to work on the database ordered on
this field. Actually, the current implementation of the indexes is
something like ISAM + B+Trees, but the name ISAM has been kept for
compatibility. NONE = when you don't do much operations on this
field.
More on the indexing: if you want a field not to
allow null values or duplicate values, you should append an '1'
or a '0' to the indexing string like here:
ISAM00 = ISAM
indexing, duplicate and null values are allowed. ISAM11 = ISAM
indexing, duplicate and null values are NOT allowed. ISAM10 =
ISAM indexing, duplicate values are NOT allowed, null values
are. ISAM01 = ISAM indexing, duplicate values are allowed, null
values are NOT. B+TREE00 = B+TREE indexing, duplicate and null
values are allowed. B+TREE11 = B+TREE indexing, duplicate and
null values are NOT allowed. B+TREE10 = B+TREE indexing,
duplicate values are NOT allowed, null values are. B+TREE01 =
B+TREE indexing, duplicate values are allowed, null values are
NOT. NONE0 = No indexing, null values are allowed. NONE1 =
No indexing, null values are NOT allowed.
As you may have
noticed, you cannot specify 'no duplicate values' for the no-index
strategy of indexing.
Notes:
you cannot have currently more than 1000 fields
in a record.
you have to have at least an index.
when you create a database, several files will
be created.
One, .dat is the real data base; then, you have
a file for every index. This was done for speed.
Rexx
connect(Rexx rName)
This method simply connects to an existing database
(rName is the name of the file).
Rexx
definefilter(Rexx rFilter)
By means of this method you can specify a filter
with which all subsequent loadrec calls
will have to comply.
Parameters:
rFilter
= an expression, which can have nested expressions in parenthesis.
Separate filter conditions with | (OR condition) and & (AND
condition).
Example: name = rob* | surname
= kol* will search for every person
whose name starts with 'rob' or whose surname starts with 'kol'.
Note: Valid filter conditions are: <fieldname>
<operator> <value>
Where <fieldname> is a
valid field name, e.g. 'Name'. <operator> can be one of
the following: =,<>,>,>=,<,<= <value>
is a valid value for that field, e.g. 'Robert'
Wildcards
are allowed only for the = and <> operators, and they are
represented by the symbol '*', which means "every number of
any character". So, Ro* can be Robert, Ronald, etc. *t can be
Matt, Robert, etc. M*t can be Matt, mount, etc. (The search is
always case-insensitive) You can use only one '*' on a given
filter (e.g. you can't query for "Name = *u*i*l*"), with
the notable exception of the form *value*, which means "find
a substring anywhere that is equal to 'value'".
Example: "Telephone = *555*" will search
for all phone numbers with a 555 in them.
Your filter
conditions will not produce anything until the next LOADREC method
is issued, from them on all the calls to LOADREC will use your
filter. When requesting records, if a filter condition is present,
the information about the absolute position on the index of the
records will not be meaningful. If you specify a filter
condition when another one is in use, the first one will be
dropped, and the most recent one will be used. Passing the null
string or the "" value will reset the filter conditions.
Rexx
delerec(Rexx[] rToDele, boolean bDelete)
Delerec is used to delete/undelete one or more
records.
rToDele[0] has to be
the total number of records to be (un)deleted, and rToDele[n] is
the ACTUAL position of the n-th record to be (un)deleted on the
database (NOT A NUMBER RELATIVE TO AN INDEX). The first record
on the database is 1 (one).
When bDelete
= 1, the records are to be deleted, when bDelete = 0, the
records are to be undeleted.
No errors will come by
deleting an already deleted record, or the other way around.
Modirec returns one of the standard RXDbase error codes.
Rexx
deletedb(Rexx rDbName)
This method deletes an entire database, indexes and
all.
Rexx
delfield(Rexx rFieldName)
With this method you can delete a field from a
database, including indexes (if any).
Note: As ever, rFieldName can be the name of the
field (ie, 'name') or the number which references the field (ie,
'4', if the fourth field is 'name'). Warning: The field will
disappear *physically* from the archive. No undo, no restore,
zippo.
Rexx
disconnect()
Simply put, this method closes all the db activities
for the database being used.
Rexx[,
] getdbinfo()
This method returns the database structure (names
and field length, along with the number of fields).
The returned values are: (let's say we stored the
contents in rRec[, ])
rRec[0, 0] --> Number of fields in
the database. rRec[n, 0] --> Length (a whole number stored
in a Rexx variable) of field 'n'. rRec[n, 1] --> Name of
field 'n'. rRec[n, 2] --> Indexing method of the field: it
can be any of the following: ISAMXX = Indexed Sequential Access
Method. B+TREEXX = B+Tree-based indexes. XX = 00, 01, 10,
11. NONEX = when you don't do much operations on this field. X
= 0, 1. See the "connect"
method for a listing of valid XX, X values.
Rexx[]
getdbstats()
This method returns some useful statistics about the
database.
This function returns an array in which:
Element
0 of the array is the db size in bytes. Element 1 is the record
size in bytes. Element 2 is the total number of records in the
database. Element 3 is the number of active records (not marked
with 'N') in the database.
Rexx[]
getdbstatus()
This method lets you retrieve the RXDbase revision
and the presence of a filter.
Returns: rVar[0] = "No Filter" or
"Filter Present" or "No DB loaded" rVar[1]
= RXDbase version number (e.g. "RXDbase 1.0 Max Marsiglietti
1997").
Rexx
globalrebuildidx()
With this method you can rebuild all the indexes of
the database currently in use. Useful to correct errors, or to
restore all the indexes after a backup+restore. (This way if you
need to backup your archives, you can backup only the .dat file,
and issueing the globalrebuildidx method after the restore you
will get back all of your indexes).
Rexx[, ]
loadrec(Rexx rIdx, Rexx rHowManyRec, Rexx rStart, boolean
bForward)
Loadrec loads in memory a number of records from the
database. If a filter has been defined with the definefilter
method, it will be used; otherwise, no record filtering will
occur.
rIdx is an existing
index; the returned records will be ordered on it. It can be a
number (i.e. rIdx = 1 means 'the index on the first field') or a
name (that field's EXACT name, i.e. "Name"). rHowManyRec
is the number of the records to be loaded, ie '72'. It can also
have the value 'ALL', meaning you want all of the records from
rStart on. rStart is the starting
record. The first record is labeled '1'. A valid value for this
variable is also 'LAST', which means 'the first record to get is
the last in the archive'. bForward
If "true", it means take rHowManyRec records starting
from rStart on (ascending order), else it means take them from
rStart back (descending order).
It returns a bidimensional
array which is structured this way:
rArray[0,0] is the
number of records that satisfy the filter
condition(s). rArray[0,1] is the number of records which are
actually being returned (please use this, and not [0, 0], to know
how many records have been returned). rArray[n,m] is the field
n of record m (m = 1 to rArray[0,0]). rArray[N+1, m] can be "Y"
or "N", depending on the given record: "Y"
means active, "N" means deleted (only active records are
returned, as of this release). rArray[N+2, m] is the position
of the m-th record on the archive (NOT on the index). rArray[N+3,
m] is the position of the m-th record on the used index (NOT on
the archive); this information is meaningless if a filter is
present.
Rexx[,]
loadrecwithfilter(Rexx rIdx, Rexx rHowManyRec, Rexx rStart,
boolean bForward, Rexx rFilter)
This method lets you perform a loadrec with a
certain filter, then reset the filter to whatever it was before.
The parameters exactly match the ones in methods
loadrec and definefilter.
Rexx
modfieldidx(Rexx rFieldName, Rexx rFieldIndexingMethod)
Modfieldidx lets you modify the indexing method for
a given, existing, field. It automatically rebuilds the index, if
passing from no index to some kind of index. It is useful if you
decide that you don't need anymore an index, or if you think that
you should have put an index on a field at database definion time,
but you didn't.
Note: rFieldIndexingMethod can be one of the
following:
ISAM00 = ISAM
indexing, duplicate and null values are allowed. ISAM11 = ISAM
indexing, duplicate and null values are NOT allowed. ISAM10 =
ISAM indexing, duplicate values are NOT allowed, null values
are. ISAM01 = ISAM indexing, duplicate values are allowed, null
values are NOT. B+TREE00 = B+TREE indexing, duplicate and null
values are allowed. B+TREE11 = B+TREE indexing, duplicate and
null values are NOT allowed. B+TREE10 = B+TREE indexing,
duplicate values are NOT allowed, null values are. B+TREE01 =
B+TREE indexing, duplicate values are allowed, null values are
NOT. NONE0 = No indexing, null values are allowed. NONE1 =
No indexing, null values are NOT allowed.
As you may have noticed, you cannot specify 'no
duplicate values' for the no-index strategy of indexing.
Rexx
modfieldlength(Rexx rFieldName, Rexx rNewLength)
This method lets you change the length of a field.
Note: All the information in excess of the new
length will be lost. Note2: As ever, rFieldName can be a number
(eg. '4' meaning the 4th field) or a name (eg. "surname").
Rexx
modfieldname(Rexx rOldName, Rexx rNewName)
This method lets you change the name of a field.
The field with name rOldName will get rNewName
name. Note: rOldName can be a number (eg. '4' meaning the 4th
field) or a name (eg. "surname").
Rexx
modirec(Rexx[, ] rToModi)
Modirec is used to modify one or more records in the
database.
rToModi is to be built
this way:
rToModi[0, 0] is the total number of records to
be modified rToModi[n, m] is field n of the m-th record to be
modified. n and m always start from 1 (one). rToModi[0, m] is
the ACTUAL number of the record in the database (NOT A NUMBER
RELATIVE TO AN INDEX) to be overwritten. This information is the
same of the [N+2, m] element in the loadrec
method. The first record in the database is marked as
'1'.
Warning: this method doesn't update single fields, it
overwrites whole records.
Note: there is not an order on
the records to be modified, i.e. you can have:
rToModi[0,
1] = 5 rToModi[0, 2] = 3 rToModi[0, 3] = 18
Returns:
An
appropriate error message is returned in case of errors, else you
will be returned a Rexx string, in which every character is either
'0' or '1', where '0' means that the specified record was not
succesfully saved (a null value where nulls weren't allowed, or a
duplicate value where there sholdn't be any) and a '1' means that
the record was succesfully saved. Example: If
we try to save 3 records and we get '010' back from modirec, this
means that only the 2nd record among the ones passed to modirec
was succesfully stored in the database: the remaining two were
discarded (probably because they had duplicated values for fields
in which we specified that no duplicate values should be allowed).
Rexx pack()
When you delete a record by means of the DELEREC
function, that record doesn't physically get deleted from the
archive, it is instead marked as 'deleted'. PACK does what the
name suggests, it permanently deletes records from the archive,
updating automatically all the indexes.
Rexx
rebuildidx(Rexx rFieldName)
This method is similar to globalrebuildidx, but must
be invoked explicitly on an index.
Note: As ever, rFieldName can be the name of the
field (ie, 'name') or the number which references the field (ie,
'4', if the fourth field is 'name').
Rexx
saverec(Rexx[, ] rToSave, Rexx rMode)
This method lets you write one or more records on
disk. All of the indexes are automatically updated.
rMode can only be
'overwrite' or 'append' (case isn't significative).
rToSave is to be built
this way: rToSave[0,0] --> number of records to be
written. rToSave[n,m] --> field n of record m.
Warning: both n,m are
to start from 1 (one). Warning:
When you specify 'overwrite', the whole database will be
overwritten (deleted), and the indexes will be
resetted.
Returns:
An appropriate error message is
returned in case of errors, else you will be returned a Rexx
string, in which every character is either '0' or '1', where '0'
means that the specified record was not succesfully saved (a null
value where nulls weren't allowed, or a duplicate value where
there sholdn't be any) and a '1' means that the record was saved
succesfully. Example: If we try
to save 3 records and we get '010' back from saverec, this means
that only the 2nd record among the ones passed to saverec was
succesfully stored in the database: the remaining two were
discarded (probably because they had duplicated values for fields
in which we specified that no duplicate values should be allowed).
Rexx
status()
With this method, you can query the internal status
of the database. Possible return values are: DBNOTSTARTED:
(can't work on nothing) READY: (no
errors) SYNTAX ERROR: (go
figure) ERROR:FILE ACCESS PROBLEMS
(something weird at file level)
History
1.16 -- Corrected a problem with large databases..
[03-mar-98] 1.15 -- Corrected a concurrency problem, added
automatical DB closing when another DB is opened, corrected
problems with loadrec and no filters present, improved query
performances. [25-jan-98] 1.14 -- Revised corrupted archive
recovery. [12-jan-98] 1.13 -- Added stricter parameter checking
for loadrec. [7-jan-98] 1.12 -- Improved the filtering
capabilities and performances.[31-dec-97] 1.10 -- Upgraded
RXDbase to Java 1.1 [12-dec-97] 1.07 -- Corrected: other minor
filter failures. Speeded up filter operations, multithreaded the
whole class (speeded up the saverec/modirec/delerec operations).
[01-jun-1997] 1.06 -- Corrected: Minor filter failures. Speeded
up filter operations. [26-may-1997] 1.05 -- Corrected the
NODUPLICATES behaviour: now also records in the middle of an
archive are correctly checked for duplicate values.
[16-may-97] 1.04 -- Corrected an error on search functions
(weren't case-insensitive). [14-may-97] 1.03 -- Corrected an
error on index handling. [8-may-97] 1.02 -- Corrected errors in
the dbf to dat utilities; Updated online reference; Enhanced
installation program; added .dbf conversion on MaxBase startup;
added automatical rebuild of indexes on MaxBase startup (only when
necessary). (4-may-97) 1.01 -- Added information for
registering the software with BTM Micro; corrected a small bug
with indexes (2-may-1997). 1.00 -- Initial release
(2-may-1997).
|