| |
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:
- Fixed length fields (this feat may be removed by
popular demand).
- ISAM and B+Tree indexing. A note about indexing:
I have dropped the idea of doing B+Tree indexing
on disk, since the current implementation is ISAM
on disk and B+Tree with linked list in memory
(which is FASTER than a simple B+Tree).
- string-oriented.
- easy to use.
Use of the RXDBase class
import 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:
- you cannot have currently more than 1000 fields
in a record [upped from 256].
- you have to have at least an index.
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.
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, 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.
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.
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: "ISAMXX", "NONEX". See
the "connect" method for
a listing of valid XX, X values.
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).
|