| | The Object Engine(tm) | C++ Class Library | and Code Generator | for The Paradox Engine(tm) | | Version 1.0 | July 22, 1993 | | Dr. Mark Brittingham | | | The Object Engine is a C++ class library that encapsulates the core functionality of the Paradox Engine database library. This encapsulation provides a large degree of OODBMS functionality to engine users. Rather than spending your time writing functions to copy data back and forth between data structures and record buffers, you need only call "Store", and "Link" methods of the object engine's persistant classes. Furthermore, these functions (and others) are written for you automatically. You simply specify the fields that will populate your new class, and the Code Generator automatically generates the class and all of its methods for you! The generated code will work under both Windows and DOS and can be compiled with Borland C++ 3.1 (a sample is included to help guide you). The library covers Alphanumeric, Short, Long, Double, Float (currency), Date, Blob and List types (see below for a definition of the List type). The generator allows you to create both primary and secondary indices. And, you can create your class instances using the primary or any secondary index to open the underlying table (including composite indices). In addition to these features, the object engine provides a unique database list class that allows you to store a heterogeneous set of persistant objects. You can construct arbitrarily complex data relationships without the need for common (relational) fields by placing pointers to these lists within persistant classes. You gain nearly all of the power of a full fledged OODBMS simply by adding the Object Engine library to your current Paradox Engine application. Plus, the library is very efficient: it adds less than 19K to your application (with speed optimization). Finally, the Object Engine's BLOB support includes two functions that simplify BLOB programming. Blob::OEPut and Blob::OEGet put and get BLOB data with one call in each direction. You simply pass in or read out the data in character arrays. When reading the data, we even create the data array for you. To use these functions, your BLOB data must be no larger than 64K bytes to use these functions. Of course, an example is included in this package (see blobmain.cpp). Also...I've fixed the bug in going to the first record of a table. (Sorry...) Distribution Policy ------------------- The files contained on this disk: objeng.cpp, objeng.hpp, manual.txt, and GEN2.EXE, are all copyrighted material. However, you may use, copy and distribute this software free of charge under the following conditions: 1. Never change any part of the Copyright statement. 2. All of the enclosed files must be distributed together. Copyright (c) 1993, Brittingham Software Design, Inc. 3. You must not distribute changed versions of the program. I do not want to be receiving correspondence about bugs that I have not produced. If you have a suggestion, contact me at the address below and I will consider your suggestion. If I incorporate your change, you will be credited in the documentation. 4. The author is not liable for any damage on your side caused by the use of this program. 5. The author has no duty to remedy for the deficiencies of the program. 6. If you wish to distribute this software with publications or a product, you must print the BSDI copyright statement somewhere on the disk or on the package. As long as those conditions are satisfied, you do not need to get the author's permission to use or to distribute the software. How to contact Brittingham Software Design ------------------------------------------ The best way to contact me is to send MAIL to me on CompuServe: 72740,2244. If you do not have a CompuServe account, you may write to me at: Dr. Mark Brittingham 15 Pheasant Ct. Flanders, NJ 07836 Using the Library ----------------- To use the library, you should first plan out your database structure. You should have a clear idea of the classes needed in your application and their interrelationships. Next, run the Gen2.exe application (it runs under Microsoft Windows(tm)) and create each of your classes. Gen2 is quite simple. A single button allows you to create a class or to edit whichever class is highlighted in a list of classes. You can save your class definitions in a format recognized by Gen2 and restore them later. Thus, you need not complete the entire set of class definitions in one sitting. When you are satisfied with the set of classes you have defined, you can generate the code to implement these classes as persistant objects with the push of a button. You will specify the directory and filename of the header and C++ files. Keep in mind that you can go back, change your classes, and regenerate your code. However, any changes YOU make to the generated code will be overwritten (unless you use another file name). Thus, it is probably best to either subclass the generated classes or at least to keep your own code separate. No further documentation is included for the generator simply because it is so straightforward. Please fire it up and see for yourself how quickly you can generate some real persistant classes. Copyright (c) 1993, Brittingham Software Design, Inc. To work with the generated code, you need to do three things: 1) include the generated header file in the appropriate code files, 2) place the generated C++ file in your project or makefile, and 3) place the objeng.cpp file in your project/makefile. Remember what you named the header and C++ files during the generate phase so you know what to include in steps 1 and 2. The objeng.cpp file is included in the Object Engine Zip file. Now you are ready to declare, manipulate, store, or retrieve instances of the classes you have defined! You never need to worry about initializing the engine, manipulating or creating tables, or reading or writing data. Of course, you do have control over Paradox Engine initialization as well as table manipulation if you need it. But, for the vast majority of your work, you need only deal with standard C++ syntax. Two Keys To Object Engine Programs ---------------------------------- Two core ideas will help you to understand how to use the library. First, to store an object in the library, simply call its "Store" method. To retrieve a database record and place it in an object, you will "link" the object to the record. This can be done either by the contents of a field (possibly a key field), or navigationally (first, next, etc.). Thus "linking" is the act of making an in-memory data structure reflect an on-disk record. Of course, you needn't think of it in terms of database records at all! The following link functions are defined: LinkToKey (one or more key fields) LinkToSKey (to link to secondary & composite keys) LinkToRecord (a record position) LinkToField (contents of a field) LinkToID (objectID - see below) LinkToFirst LinkToLast LinkToNext LinkToPrev The second key idea is found in the "objectID". Each object has an objectID associated with it. When you link an object to a particular database record, your object is given the record's unique objectID. You need to keep this in mind when working with persistant classes. For example, if you create a new object using the copy constructor or set one object to another (using '='), then both will have the same objectID. If you change these two objects separately and save them both, then the record will reflect the last one stored. That is, storage is mediated by the objectID. An object without an objectID will be stored as new (assuming that there are no conflicting key fields). An object whose objectID matches a record found in the database will update that record. If you simply keep in mind that an object has an identity that you must respect, you will have no problem using the library. Copyright (c) 1993, Brittingham Software Design, Inc. *************************************************************************** Persistant Class Functions - Introduction When you generate code using gen2.exe, you will see that your class definition: a) is derived from "PersistClass", b) has one or more variable declarations, c) has one or more matching "SetXXX" functions to set the variables, d) has one or more "GetXXX" functions to get the value of variables, and e) has only six other methods (2 Constructors, operator=, Store, Retrieve, and LinkToKey). If a class contains a list (an "OEList") of other persistant classes or if it contains BLOB fields, then there is no corresponding "SetXXX" function and the "GetXXX" function returns a non-const pointer to the actual list or a Blob class instance. In both cases, you work with your data by getting a reference to an OEList or Blob instance and using method calls specific to these objects. All other returned values are defined CONST to ensure encapsulation. You can change the "SetXXX" functions to reflect any validity checking or dependent variable manipulations that your class requires. Since the OEList and Blob classes already feature their own encapsulation (and since the code generator architecture made it a pain to make the list variables public), a non-const reference to an instance is returned from the "GetXXX" function. Blobs are new to version 1.0. In addition to the Set and Get functions and the six defined with the class, your class inherits many functions for linking to various records as well as functions for testing status, returning Paradox Engine variables (table and record handles, etc.), and more. Functions that return a value will return Paradox Engine error codes (PXSUCCESS, PXERR_XXX). In all cases (functions, operators, constructors), the variable "status" will be filled with the most recent Paradox operation's return value (use GetStatus() to inspect). Thus, you will need to be aware of the Paradox Engine's error codes. You may create objects with an indexID so that the table they work with will be sorted by a secondary key. That is, a class' constructor takes an optional argument (an integer) corresponding to a secondary key. If you call a class constructor with a secondary key and then step through the table using LinkToNext, the records will be ordered by that key. This feature is new to version 1.0. Copyright (c) 1993, Brittingham Software Design, Inc. *************************************************************************** Class Function Documentation For purposes of clarity, assume that you have declared a class "Client." The following functions would be defined: //------------------- Native (Generated) Functions ------------------------ //------------------------------------------------------------------------- Constructor: Client(int indexID = 0) [format: (int indexID)] Creates an instance of Client. If the client database does not exist, it is created. If it is not open, it is automatically opened. The object does not reflect any record in the database when first created. All strings and lists are empty, all scalar values are set to zero. The database is opened on the index given by indexID. If indexID is omitted, it defaults to 0 (opened on primary index or by physical order). Otherwise, the secondary index given by indexID is used (the table will appear to be sorted in secondary index order). To open a table on a composite secondary key, you will have to open a table with no index. Then call "GetFieldHandle" with the name of the composite key to get its numerical field handle (the name is all you know ahead of time). "GetFieldHandle" is a method of PersistClass and is documented below. Since key handles stay the same (except, possibly, if you drop and then re-add them), you could do this in one sample run and then just use the returned field handle (as a constant) for all subsequent runs. //------------------------------------------------------------------------- Constructor: Client(Client& val) [the copy constructor] Creates a new, identical instance of Client. The client database will be created and open since you must have already created a client to pass in! The new object reflects all of the values of the passed in object. OELists are not copied! The same OEList pointer is now used in both objects. If you make a change to the list using the first object, you are also changing the list used by the second. Since the list is meant to reflect the database, this is exactly as it should be! //------------------------------------------------------------------------- Operator: Client &operator=(Client &) [the assignment operator] See the copy constructor. Best used during the declaration of a reference. //------------------------------------------------------------------------- Private function: int Retrieve [note "Private", do not make public!] Copies from the database record to the class' variables. Called from all of the "Link..." functions (see below). Return Value: PXSUCCESS or other engine error code. Copyright (c) 1993, Brittingham Software Design, Inc. //------------------------------------------------------------------------- Function: int Store() [see PXRecInsert, PXRecUpdate] Moves the contents of the object into the database. If the object is already linked to a database record (either from a link function or because it was copied from another object) then this will UPDATE the database record. Any object possessing a non-zero objectID is assumed to be linked to the matching db record. If the object is not yet linked to a database record (objectID is equal to 0), then calling Store will add it to the database (PXRecInsert). Note that this function reflects the documentation for PXRecUpdate and PXRecInsert: an attempt to enter a duplicate key is an error, a non-keyed table insert will place the record before the current record, etc. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToKey(int numkeys, int mode) [See also, PXSrchKey] When you define a class, you can specify one or more of its variables (starting with the first), as keys. A key variable corresponds to key fields in the database. To search using key values, you place these values in the object's key variables and call this function. For example, if you have three key fields and want to search on the first two, you should place values for these first two key fields in the corresponding variables and call this function with numkeys = 2. Set Mode equal to any mode legal under the PXSrchKey documentation (SEARCHFIRST, etc.) according to your search requirements. For example: ... Client.ID = 1000; if (Client.LinkToKey(1, SEARCHFIRST) == PXSUCCESS) Note: if the field is not keyed, this function does nothing but return an error. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToSKey(char *fldName, int mode) [See also, PXSrchFld] If you define one or more secondary keys in the Gen2 program, secondary keys will be automatically created when your table is created (by default, these are INCSECONDARY keys: see Paradox Engine documentation). You can use these keys by filling the appropriate fields with your search criteria and calling the LinkToSKey function. This function takes the name of the field (or composite key) and the mode (SEARCHFIRST, SEARCHNEXT, etc.). Note that the case-insensitive and composite fields you create are accessed by name (by default, Gen2 creates case-insensitive names as "CI". If you forget the name of a composite or case-insensitive key, you can look in your class constructor. For each such key there will be a "RegisterCompKey" call; the key name is the fourth argument to this call. The MAIN.CPP example file demonstrates a call to LinkToSkey. Copyright (c) 1993, Brittingham Software Design, Inc. //------------------- Inherited Functions --------------------------------- //------------------------------------------------------------------------- Function: int LinkToField(char *fieldname, void *value, int mode) [See PXSrchFld] To search for a specific value in a specific field, pass in the fieldname, the value (cast to a void pointer), and the search mode. For example: client.LinkToField("LNAME", (void *)"Brittingham", SEARCHFIRST) client.LinkToField("ID", (void *)&i, SEARCHFIRST) Note that the value must be a void pointer. If you really want overloaded functions, let me know. It takes up more space but might be worth doing. Note also that searches using compound keys are on the way... Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToRecord(int position) [See PXRecGoto] Make object reflect contents of the nth record where n = position. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToID(const long &ID) [See PXSrchFld] Explicitly set object to database record with objectID = ID. Should not generally be needed. Note, no mode argument since objectIDs are unique. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToFirst() Make object reflect contents of first database record. If database is empty, contents are not changed and error code is returned. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToLast() Make object reflect contents of last database record. If database is empty, contents are not changed and error code is returned. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToNext() Make object reflect contents of next database record. If there is no next record, contents are not changed and error code is returned. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int LinkToPrev() Make object reflect contents of previous database record. If there is no previous record, contents are not changed and error code is returned. Return Value: PXSUCCESS or other engine error code. Copyright (c) 1993, Brittingham Software Design, Inc. //------------------------------------------------------------------------- Function: void Unlink(); Removes the objectID from the object. This allows you to copy an object and then unlink it from the database record. If you then Store the object it will go in under a new objectID (assuming there is no key conflict). Return Value: None //------------------------------------------------------------------------- virtual int Destroy() [See PXRecDelete] Deletes the database record to which this object corresponds. If the object has not been linked, it returns PXERR_RECNOTFOUND. Does not delete the object or change any of its values. Note that deleting an object does not remove it from the database. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- Function: int GetFieldHandle(char *fldName, FIELDHANDLE &fieldHandle) Retrieves the handle of the field named by fldName and places it in fieldHandle. Useful for figuring out what handle has been assigned to a composite key. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- int NRecs(RECORDNUMBER & nrecs) [See PXTblNRecs] Retrieves the number of records in the table that the object works with. The number of records is placed in the nrecs parameter. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- const int GetStatus() Returns the status value. Status is set by the most recent Paradox Engine function. This function is useful for testing the success of functions that don't return a status value (constructors and operators) although it can be used after any Object Engine call. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- TABLEHANDLE GetTblHdl() Returns the Paradox Engine table handle. Can be used if you want to bypass the library and work directly with the table. Since the Object Engine does not provide direct table manipulation (copy, delete, etc.), you may find this function useful. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- RECORDHANDLE GetRecHdl() Returns the Paradox Engine record handle. Can be used to bypass the library and work directly with the record buffer. Should not be needed unless you want to work with multi-field secondary indices (which will be available in the next release anyhow). Return Value: PXSUCCESS or other engine error code. Copyright (c) 1993, Brittingham Software Design, Inc. //------------------------------------------------------------------------- const char *GetTblName() Returns the name of the table to which this object is attached. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- long GetObjectID() Returns the objectID. Should not be needed. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- char GetDeleteFlag() char SetDeleteFlag(char flag) GetDeleteFlag and SetDeleteFlag are used in conjunction with OELists. When an OEList is deleted or cleared, it will check the deleteFlag for each of the elements that is points to. If the deleteFlag is TRUE, the object will be deleted. Otherwise it will not. In both cases, any reference to the object is removed from the OEList. Thus, if you create a pointer to an object that you get from an OEList, you should set its deleteFlag to FALSE if you want to delete the list but keep the pointer. Return Value: PXSUCCESS or other engine error code. //------------------------------------------------------------------------- int OEPutDate(const int &fieldno, const struct tm &date) and int OEGetDate(const int &, struct tm &) Used in the classes derived from PersistClass to place dates into specific record buffer fields. Used to simplify the process of generating code. You should never need to use these functions since you will not work directly with record buffers. Return Value: PXSUCCESS or other engine error code. Copyright (c) 1993, Brittingham Software Design, Inc. *************************************************************************** Object List (OEList) Methods - Introduction You will generally use OELists by declaring a field to be of type '^' in the code generator. This will generate all of the code you need to Store and Retrieve nested classes. For example, if you have multiple phone numbers for a client, you may want to create a nested list of phone number objects and store it with a client record. To pursue the example, assume that you have declared a class "Client" and a class "Phones." You want to associate zero or more phones with a client but don't want to use a unique relational field in both tables since a given phone might be used by more than one client (a husband and wife, for example). In this case, you can declare the Client class with an OEList called "phones." When working with a client you can add a phone number in the following manner: Client client; Phone phone; client.LinkToField("LNAME", (void *)"Brittingham", SEARCHFIRST); phone.Setnumber("555-1212"); client.Getphone()->Insert((PersistClass *)&phone); client.Store(); Note the following important points: The phone object Inserted into the list and the link to the client object are both immediately Stored. You do NOT need to call Store on an object that you place in an OEList (whether or not the list is owned by another PersistClass object). This means that changes you make to a class's OEList variables cannot be undone simply by not storing the object. You CAN have arbitrarily nested OELists. For example, the Phone object could have a list of people that the phone number serves. This list may include the client object defined above. We avoid recursive infinite loops by NOT retrieving the data in a list until you take an action (such as inserting an object). This also avoids retrieving a all of the data associated with a complex object with nested OELists if you just want to see a single field. If you want to make sure that all of the data associated with an OEList is in memory immediately, you can use the OEList::Init() function. OELists have listIDs (just as objects have objectIDs). When you place objects in an OEList, the objectID of the object and the listID of the list are associated and stored. If you later call the OEList constructor with a listID (and call Init()), the system will search for all objects that were ever put on the list and pull them into memory. This is how OELists are stored on PersistClasses: the listID is stored in the object's table as a LONG. When the object is retrieved, the listID is used to construct a new OEList (see generated source code). The OEList is both a container and an iterator. This is achieved by having the "()" (function) operator return the "current" object on the list. The "++" operator moves to the next object on the list. The Reset() function makes the first object on the list the current object. For example, you can do the following: Copyright (c) 1993, Brittingham Software Design, Inc. OEList objlist; PersistClass* temp; Foo* footemp; objlist.Insert((PersistClass *)fooptr1); //First position objlist.Insert((PersistClass *)fooptr3); //Second position objlist.Insert((PersistClass *)fooptr4); //Third position // objlist() would now return a pointer to fooptr4. // Move to first position before iterating. objlist.Reset(); while ((temp = objlist++) != NULL) { ((Foo *)temp).PrintSelf(); <- Some user-declared function } // OR use a for loop... for (objlist.Reset(); footemp = (Foo *)objlist(); objlist++) { // Insert after fooptr object with ID of 1 (id is user-declared) if (footemp->Getid() == 1) { objlist.Insert((PersistClass *)fooptr2); break; } } Copyright (c) 1993, Brittingham Software Design, Inc. *************************************************************************** OEList Class Function Documentation //------------------------------------------------------------------------- Constructor: OEList() Create a new OEList. This object will be a simple shell without ID or any objects inside. //------------------------------------------------------------------------- Constructor: OEList(long inID); Create a new OEList. This object will be a simple shell but will have the given ID. When Init() is called, all objects associated with the given listID will be pulled into memory. //------------------------------------------------------------------------- int Init() Instantiate the given OEList. If no listID has been assigned, generate a new (unique) listID. If a listID exists, pull out all associated objects from the database and store them in memory. Return Value: TRUE or FALSE. //------------------------------------------------------------------------- Destructor: ~OEList(); Deletes all of the objects pointed to by the OEList EXCEPT those whose deleteFlag is FALSE. Also, this function WILL CAUSE AN ERROR WINDOW OR MESSAGE to pop up if you attempt to delete a list that has multiple pointers to it. That is, whenever an object is copied, it will copy any OEList pointers. In order to be a good citizen, it will also inform each OEList (via the AddReference function, see below) that a new pointer exists. When you want to delete a list, you should make sure that you check this pointer count. The RIGHT way to delete a list is: if ((oelist != NULL) && (oelist->DeleteOK() == TRUE)) delete oelist; The code generator automatically puts this line into the destructors of all classes that have OEList elements. In general, you do not need to worry about this at all unless you are declaring your own lists and creating multiple pointers. If you just use the code generated by Gen2, you are guaranteed never to run into any problems. //------------------------------------------------------------------------- void Insert(PersistClass *curelem) Inserts a persistant class into an OEList. If you put an element into the OEList and want to keep it around even if the list goes away, call the object's SetDeleteFlag function with an argument of FALSE. If you delete an object pointed to by a list you may crash your program if you work with the list later. //------------------------------------------------------------------------- void Remove() Removes the current object from the list (you change the "current object" by iterating through a list- see above). If the deleteFlag is TRUE, this function deletes the object from memory. Does NOT destroy the object's corresponding database record. The on-disk representation of the list is immediately updated to reflect this removal. Copyright (c) 1993, Brittingham Software Design, Inc. //------------------------------------------------------------------------- PersistClass *operator()() Returns the current object. Does not increment or otherwise change the state of the list. //------------------------------------------------------------------------- PersistClass *operator++() Returns the current object and then increments the "current object" pointer. //------------------------------------------------------------------------- void Reset() Moves the "current object" pointer to the first element of the list. //------------------------------------------------------------------------- int Count() Returns the number of elements in the OEList. Do not use this for iterating unless you are SURE that you will not be changing the list in your iteration. //------------------------------------------------------------------------- void Clear() Removes all of the elements on the list. See the Remove() function. //------------------------------------------------------------------------- void AddReference() If you will be creating your own pointers to a list, this function is used to inform the list of your pointer. //------------------------------------------------------------------------- char DeleteOK() Decrements the reference count and returns it. This is the opposite of AddReference. You should not call this function unless you are removing your pointer. Also, you should delete the list if this function returns 1. (See the documentation for the destructor). //------------------------------------------------------------------------- const int GetStatus() Returns the status of the list. Can be any of: NOTINIT, READY, or FAIL. //------------------------------------------------------------------------- const long GetListID() Returns the listID. This number is used in a table to keep track of the associations between lists and objects. Copyright (c) 1993, Brittingham Software Design, Inc. *************************************************************************** The Blob Class - Introduction The Blob class allows you to store large text or binary objects in the Object Engine database. Most of the Object Engine Blob methods provide a thin encapsulation around the corresponding Paradox Engine function calls. They do simplify blob programming, however, since they take care of the record and field handles for you. The real value added lies in the OEGet and OEPut functions. Not only do these functions provide one-call storage and retrieval of blob data, they also illustrate blob programming techniques that you will use when using the more basic functions. You will need the more primitive functions only if you need to store blobs whose size exceeds 64K bytes. You will use Blob objects by declaring a field to be of type 'Mxxx', 'Bxxx', 'Fxxx', 'Oxxx', or 'Gxxx' in the code generator (where xxx is some positive integer). When you generate code, one or more of the fields in your class will be instances of class "Blob". To work with the blob data, you will call methods attached to this class instance. For example, if the field "imageblob" is a blob field, the following would store data in the blob and write the blob to the "public" area of the blob file (see the PXEngine documentation for a discussion of Private and Public blobs): // Put the buffer into the blob using the ObjEngine shortcut... dbclass.Getimageblob().OEPut(imagebuffer, BUFLEN); // NOTE: Must STORE the class before its blob is publicly stored! dbclass.Store(); Note the "Getimageblob" function. This is generated by the Gen2 program and uses the standard syntax: "Get". Since it returns a reference to a Blob instance, it is easy to call the OEPut function. An illustration of Blob programming using the OEGet and OEPut functions can be found in the blobmain.cpp file in this package. Of course, all of the PXEngine blob functions are covered as well. These functions follow the same naming convention as the Paradox Engine except that "PXBlob" is stripped from the front of the function name. Since these are methods of the Blob class, the prefix is redundant. Their arguments are also the same as the Paradox Engine and in the same order EXCEPT the RECORDHANDLE and FIELDHANDLE arguments are always omitted. Record and Field are implied by the Blob object you are using (the Persistant Object you are working with holds the record handle, the Blob instance within the persistant object gives the field handle). These functions are documented below. Copyright (c) 1993, Brittingham Software Design, Inc. *************************************************************************** Blob Class Function Documentation All of the functions in this category that return a value (except operator=) return the last Engine state (PXSUCCESS, PXERR_XXX). //------------------------------------------------------------------------- Constructor: void Blob(OEHandle &, FIELDHANDLE) You should never have a reason to use this constructor. Leave it alone- its bad for your health. This function is used in the persistant class constructor once we know some facts about the table. The FIELDHANDLE given to this function corresponds to the field given in Gen2. The OEHandle argument is responsible for letting a Blob know which table record it works with. //------------------------------------------------------------------------- Function: Blob &operator=(const Blob &inblobobj) Used in the Persistant Object operator= so we can just set one blob equal to another without worrying about internal variables. //------------------------------------------------------------------------- Function: int Clone() See PXBlobClone: creates a private blob so no one else screws you up by changing a blob while you are in the middle of reading one. //------------------------------------------------------------------------- Function: int Close(int accept) See PXBlobClose: Closes blob opened by OpenRead or OpenWrite. Accept determines the final disposition of blob (see PXEngine doc!). //------------------------------------------------------------------------- Function: int Drop() See PXBlobDrop: Clears space taken by a blob. //------------------------------------------------------------------------- Function: int OpenRead() See PXBlobOpenRead: Prepares a blob for reading (opens a blob handle). Don't worry about the blob handle when you open a blob! I automatically keep it around for your next Get or GetSize operation so you don't even have to know that one exists. //------------------------------------------------------------------------- Function: int OpenWrite(unsigned long size, int saveCurrent) See PXBlobOpenWrite: Prepares a blob for writing (opens a blob handle). Again, don't worry about the blob handle, that's why you have me around... See PXEngine documentation for saveCurrent information. //------------------------------------------------------------------------- Function: int GetSize(unsigned long *size) See PXBlobGetSize: Returns the size of a blob you have opened (usually for reading). Generally, this function is what tells you how many times you have to call Get. Copyright (c) 1993, Brittingham Software Design, Inc. //------------------------------------------------------------------------- Function: int Get(unsigned size, unsigned long offset, void* buf) See PXBlobGet: Puts blob data into buf. All of the handles (record, field, and blob) are taken care of for you. Called in chunks to get all of the data (limit 64K at a time). If you know you will only need one Get, then use OEGet and skip all the rest of this stuff. //------------------------------------------------------------------------- Function: int QuickGet(int bufsize, void* dest, int* bytesRead) See PXBlobQuickGet: Grabs whatever header data you've stored directly in the table (up to 240 bytes). //------------------------------------------------------------------------- Function: int Put(unsigned size, unsigned long offset, void* buf) See PXBlobPut: Puts buf into private blob. All of the handles (record, field, and blob) are taken care of for you. Called in chunks to put all of the data (limit 64K at a time). If you know you will only need one Put, then use OEPut and skip all the rest of this stuff. //------------------------------------------------------------------------- Function: int OEPut(char *buff, unsigned size) See example above and source code. Just stick "buff" in a blob. Doesn't get written to the "public" area (sort of like "written to the table") until you do a "Store" on the persistant object to which the blob belongs. //------------------------------------------------------------------------- Function: int OEGet(BlobData &bdata) Hey, this Blob stuff is easy after all, isn't it? Grab a blob using this function. The BlobData structure is a struct: typedef struct { char *data; <- Holds the blob data unsigned size; <- Holds the size of the blob } BlobData; This is used so that you will have your data and know how big it is too... Note that when you create an instance of BlobData before calling OEGet, the "data" field is empty (points to nothing). When you call OEGet, I create a buffer of the appropriate size, make "data" point to it, and place the buffer size in "size". Finally, I read the blob data into the "data" field of the BlobData struct and return. YOU have to remember to delete the memory that "data" points to. After all, I can't do everything for you. For an example of OEGet see the "blobmain.cpp" file. //------------------------------------------------------------------------- Function: int GetStatus() const Returns the status value. This value is always set to the return code from the last PXEngine function call (which is also what most functions return). Especially useful for testing the result of a constructor call! //------------------------------------------------------------------------- Function: int GetRHdl() const and int GetFHdl() const For internal use. You can use these to tell you what the blob thinks its handles are. Copyright (c) 1993, Brittingham Software Design, Inc. *************************************************************************** Object Engine Methods of Interest The object engine class has a number of methods that may be of interest in your database programming. First, the constructors for this class are responsible for setting the Paradox Engine defaults and initializing the engine. When Gen2 generates code for you, a static declaration of a global "objeng" variable is placed at the top of the generated file. You can change the arguments to this declaration but you must not move it or change its scope (it must remain global). The advantage of this approach is that everything needed to work with data tables is declared and initialized without you having to worry about anything! The code is available in Objeng.cpp -- you should certainly browse around the constructors so you understand the relation between the Object Engine and the Paradox initialization routines. //------------------------------------------------------------------------- Windows Full Constructor: ObjEng(char *clientName, int shareMode, int bufsize, int maxTables, int saveChanges, char *tableDirectory) Windows Simple Constructor: ObjEng(char *clientName) DOS Full Constructor: ObjEng(int bufsize, int maxTables, int saveChanges, char *tableDirectory) DOS Simple Constructor: ObjEng() Under Microsoft Windows, the Paradox Engine requires that each application register a client name. Thus, both Windows constructors require this field. The engine's share mode (PXSINGLECLIENT, PXEXCLUSIVE, PXSHARED) determines how many applications can access the engine and/or a given table (see documentation for PXWinInit). The simple constructor (which is used by default) uses a share mode of PXSHARED. In both Windows and DOS, you may want to consider changes to the default engine settings. In this case, you will work with the "full" constructors. See the documentation for the PXSetDefaults function to understand the "bufsize" and "maxTables" settings. In keeping with my own experience, I use a maxRecBufs setting of 2 X maxTables. If you are allocating more than 2 record buffers per table on average by opening tables on secondary indices, be SURE to alter the objeng.cpp source code! In general, feel free to alter the ObjEng constructors to reflect your view of the engine's initialization needs (that's why you have the source!). The saveChanges parameter sets a global default for the "saveEveryChange" argument to PXTblOpen. That is, whenever a table is opened, it will either be told to save every change to disk as you make it or to buffer changes. This parameter allows you to specify which. You cannot change this parameter by table: it is global to all tables in Object Engine (if this is a hassle, let me know). The tableDirectory parameter tells the system which directory will hold the tables when they are created. By default (the simple constructors), this parameter is set to ".\\" (the current directory). Remember to use two slashes instead of one where applicable. Copyright (c) 1993, Brittingham Software Design, Inc. //------------------------------------------------------------------------- Functions: int ObjEng::DropKey(char *tblname, FIELDHANDLE fldID) int ObjEng::RegisterKey(char *tblname, int nIdxFields, FIELDHANDLE *IdxFieldHandles, int idxType) int ObjEng::RegisterCompKey(char *tblname, int nIdxFields, FIELDHANDLE *IdxFieldHandles, char *keyName, int idxType, int caseMode) These functions add or drop keys from the named tables. You cannot use the RegisterXXX functions while the corresponding tables are open. They can safely be used in the constructor in the IsRegistered block between "EnsureExist" and "Register". See the generated constructors for examples of how to use these functions. The "idxType" arg is PRIMARY, SECONDARY or INCSECONDARY (PRIMARY not allowed in RegisterCompKey). The "caseMode" argument is either CASESENSITIVE or CASEINSENSITIVE. The DropKey function is a wrapper for the Engine's PXKeyDrop function. //------------------------------------------------------------------------- ** Please avoid using any other Object Engine class methods. These are for internal book-keeping only and their inappropriate use may result in corruption of your databases.