--- /dev/null
+Notice:
+ Fields and Table names are remapped to:
+ first char Upper case, the rest are lower case
+
+The code generator (x.C) processes limited schema information and
+ creates code which operates a base class Db as schema describes.
+
+ x.C reads from stdin, and has 1 optional parameter (theDb class name)
+ comments are whitespace, form /*comment*/ or -- comment... <cr>
+ ./a.out < schema.txt <class_name=defaults to theDb>
+ this produces a source file s.C containing all
+ * table definitions in the form of: <Tbl>
+ constructed as: Entity Tbl(db);
+ * default locators in the form of <tbl>
+ constructed as: TblLoc tbl(Tbl);
+ <Tbl> = table name, first char capitalized.
+ <tbl> = table name, all lower case
+ * ikey_<Key_name> and rkey_<Key_name> class definitions
+ * key cmpr functions for ikey_<Key_name> and rkey_<Key_name>
+ * EntityObj functions: Allocate, Construct, Destruct, Deallocate;
+ * theDb class definition, with base class Db with
+ constructor and functions create, open, and close
+
+ x.C allowed input data as follow:
+ items in [] are optional
+
+** CREATE TABLE table;
+* table = <table_name> ( <member> [, <member> ...] )
+* member = <field_name> <dtype> [<xtype> ...]
+* dtqual = ( [0-9...] ) | [UNSIGNED | SIGNED | ZEROFILL]
+* dtype0 = BIT | TINYINT | SMALLINT | MEDIUMINT | INT | INTEGER | BIGINT |
+ REAL | DOUBLE | FLOAT | DECIMAL
+* dtype1 = DATE | TIME | TIMESTAMP | YEAR |
+ CHAR | VARCHAR [BINARY] | BINARY | VARBINARY | BLOB | MEDIUMBLOB |
+ LONGBLOB | TINYTEXT | TEXT | MEDIUMTEXT | LONGTEXT | BOOL | BOOLEAN |
+ ENUM ( <enum_value> [, <enum_value> ...] )
+* dtype = <dtype0> [<dtqual>] | <dtype1>
+* xrefer = <table_name> ( <key_field> [, <key_field> ...] )
+ ON [DELETE | UPDATE] [RESTRICT | CASCADE | SET NULL | NO ACTION]
+* def_int = [+|-][0-9...]
+* def_dbl = <def_int>[.[0-9...]]
+* def_str = '[notany(') | \any() ...]' | "[notany(") | \any() ...]"
+* def_value = <def_str> | <def_int> | <def_dbl> | NULL
+* xtype = [NOT] NULL | DEFAULT <def_value> | AUTO_INCREMENT |
+ UNIQUE [PRIMARY] [KEY] | PRIMARY [UNIQUE] [KEY] |
+ REFERENCES xrefer
+
+** CREATE online modifier INDEX index;
+* index = <index_name> ON <table_name> ( <key_field> [, <key_field> ...] );
+* online = [ONLINE | OFFLINE]
+* modifier = [UNIQUE | FULLTEXT | SPATIAL]
+
+
+note: C++ type Schema field type
+ unsigned char: BOOLEAN, BIT, BINARY
+ [unsigned] char: CHAR, TINYINT
+ [unsigned] short: ENUM, SMALLINT
+ [unsigned] int: DECIMAL, MEDIUMINT, INT, INTEGER
+ [unsigned] long: BIGINT
+ double: REAL, DOUBLE
+ float: FLOAT
+ char[n]: DATE[8], TIME[6], YEAR[4] TIMESTAMP[14], DATETIME[14]
+ char[]: VARCHAR, TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT
+ unsigned char[]: VARBINARY, TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB
+ not implemented: SET
+
+examples:
+
+CREATE TABLE user_newtalk (
+ user_id int NOT NULL default 0,
+ user_ip varbinary(40) NOT NULL default '',
+ user_last_timestamp varbinary(14) NULL default NULL
+) ;
+CREATE INDEX un_user_id ON user_newtalk (user_id);
+CREATE INDEX un_user_ip ON user_newtalk (user_ip);
+
+CREATE TABLE text (
+ old_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ old_text mediumblob NOT NULL,
+ old_flags tinyblob NOT NULL
+) MAX_ROWS=10000000 AVG_ROW_LENGTH=10240; (MAX_ROWS,AVG_ROW_LENGTH are ignored)
+
+
+
+To initialize/open a db instance:
+
+#include <cstdio>
+#include "db.h"
+#include "s.C"
+
+theDb db;
+
+int main(int ac, char **av)
+{
+ if( db.create(dfn) ) {
+ fprintf(stderr,"create_db failed\n");
+ exit(1);
+ }
+ if( db.open(dfn) ) {
+ fprintf(stderr,"open failed\n");
+ exit(1);
+ }
+ return 0;
+}
+
+
+Normally, almost everything is done with Key classes and locators.
+There is a default locator for every entity (lower case tbl) in theDb.
+ defined similar to: Entity Tbl(db); TblLoc tbl(Tbl);
+
+Locators provide access to the record member fields:
+ Access is provided by the .member() function.
+ value = tbl.Field(); // read access
+ tbl.Field(value); // write access
+ tbl.size_Field(); // size(value)
+ tbl._Field(); // addr(value)
+ tbl.Field(value,sz); // write access to varObj/blob field
+ tbl.set_wr(); // mark dirty
+
+
+
+To create database records:
+
+void insert()
+{
+ db.tbl.Allocate(); // allocates a Tbl record assigned to tbl locator
+ db.tbl.Field1(value1); // assignes values to new Tbl record fields
+ db.tbl.Field2(value2); // ...
+ db.tbl.Construct(); // runs theDb record constructor
+}
+
+the record constructor is normally generated by x.C and typically:
+ if_err( insertProhibit() ); // run consistancy checks
+ if_err( construct() ); // load id and reference
+ int id = this->id();
+ { rkey_Tbl_Key1 rkey(*this); // add all keys associated to new id
+ if_err( entity->index("Tbl_Key1")->Insert(rkey,&id) ); }
+ if_err( insertCascade() ); // run insert cascade functions
+ return 0;
+
+
+To access database records:
+ The TblLoc::ikey_Field key classes use immediate data for key values
+ The TblLoc::rkey_Field key classes use the current record for key values
+
+The Db::Key class provides:
+ int Find(); // immedate ikey_ only
+ int Locate(int op=keyGE); // immedate ikey_ and relative rkey_
+ int First(); // relative rkey_ only
+ int Last(); // relative rkey_ only
+ int Next(); // relative rkey_ only
+ int First(pgRef &pos); // relative rkey_ only
+ int Next(pgRef &pos); // relative rkey_ only
+
+To Locate/Find a table record (= EntityObj), use:
+
+ theDb db; db.open("/path/the.db");
+ int ret = TblLoc::ikey_Key1(db.tbl, key).Find();
+ if( ret != 0 ) printf(" not found, ret = %d\n",ret);
+ printf(" the record data is now %d\n", ++db.tbl.Data());
+ db.commit();
+ db.close();
+
+** NOTE: <key field parameters> will be automatically constructed from
+ basic types, but for varObj types they should be explicity constructed
+ as in:
+ int ret = TblLoc::ikey_Key1(db.tbl, TblObj::t_Key1Field1(key, ksz)).Find();
+
+Locate may be called with op=keyLT,keyLE,keyEQ,keyGE,keyGT
+ the index will be positioned to the appropriate boundry
+ keyEQ is mapped to keyLE when used with Locate
+
+First/Next have a default cursor position associated to the index.
+First/Next may return/use a cursor position locator which may be
+ used if multiple simultainious cursor positions are needed.
+
+To iterate use Locate/First to set the cursor at a table record
+ then use Next to iterate until need records are exhausted.
+
+ theDb db; db.open("/path/the.db");
+ TblLoc::rkey_KeyField1 key1(db.tbl);
+ if( !(ret=key1.First()) ) do {
+ printf(" the record data is now %d\n", ++db.tbl.Data());
+ } while( !(ret=key1.Next()) );
+ db.commit();
+ db.close();
+
+
+
+To delete database records:
+
+ theDb db; db.open("/path/the.db");
+ while( !db.tbl.Last() ) {
+ db.tbl.Destruct(); // run theDb record destructor
+ db.tbl.Deallocate(); // release db storage assigned to db.tbl
+ }
+
+the record destructor is normally generated by x.C and typically:
+ if_err( deleteProhibit() ); // run consistency checks
+ { rkey_Tbl_Key1 rkey(*this); // remove all keys associated to id
+ if_err( entity->index("Tbl_Key1")->Delete(rkey) ); }
+ if_err( destruct() ); // delete id and reference
+ if_err( deleteCascade() ); // run delete cascade functions
+ return 0;
+
+
+There are also "media" keys, which are bit field key records which
+are organized by total "weight" = unsigned sum of bit fields. The
+weight is recursively subdefined in left/right subfields, so that
+all of the key record is encoded. This usually requires a little
+over 3 times as much storage. This kind of key can index audio/video
+data (and others, biometric, dna, images...). The key data can be:
+built, recovered, and compared for both magnitude and error as sum
+of squared/linear error. The work on this not complete at this time.
+to insert (8 bit fields):
+ int ksz = Db::mediaKey::byte_size(sz, 8);
+ uint8_t *key = db.Tbl._key1(ksz);
+ Db::mediaKey::build(key, dat, 8, sz);
+
+to access (8 bit fields):
+ int ksz = Db::mediaKey::byte_size(len, 8);
+ uint8_t key[ksz];
+ Db::mediaKey::build(key,dat,8,len);
+ PrefixLoc::ikey_Prefix_key_data ikey(db.prefix, key);
+ if( (ret=ikey.Find()) != 0 ) {
+
+
+
+
+
+Example:
+
+Makefile:
+
+CPPFLAGS := -Wall -ggdb $(CFLAGS)
+LDFLAGS :=
+
+all: prog
+
+clean:
+ rm -f *.o xsch prog
+
+prog: prog.o db.o
+ g++ -o $@ $(LDFLAGS) $+
+
+s.C: x.o sch.txt
+ g++ -o xsch x.o
+ ./xsch < sch.txt
+
+db.o: db.C db.h
+prog.o: prog.C prog.h s.C db.h
+x.o: x.C
+
+
+sch.txt:
+
+CREATE TABLE a_tbl (
+ a_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ a_name varchar(255) binary NOT NULL default '',
+ a_data int
+);
+CREATE UNIQUE INDEX a_names ON a_tbl (a_name);
+CREATE UNIQUE INDEX a_datum ON a_tbl (a_data);
+
+
+prog.C:
+
+#include <cstdio>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "db.h"
+#include "s.C"
+
+theDb db;
+
+void chk(int ret, const char *msg)
+{
+ fprintf(stderr,"fail %s = %d\n",msg,ret);
+ exit(1);
+}
+
+void insert(const char *nm, int dat)
+{
+ chk( db.a_tbl.Allocate(), "Allocate");
+ chk( db.a_tbl.A_name(nm), "A_name(nm)");
+ chk( db.a_tbl.A_data(dat, "A_data(dat)");
+ chk( db.a_tbl.Construct(), "Construct");
+}
+
+int main(int ac, char **av)
+{
+ const char *dfn = ac > 1 ? av[1] : "the.db";
+ chk( db.create(dfn), "db.create");
+ chk( db.open(dfn), "db.open");
+
+ // insert some data
+ insert("a borrower", 321);
+ insert("nor lendor", 232);
+ insert("be", 123);
+
+ chk( db.commit(), "commit()");
+ chk( db.close(), "close()");
+ chk( db.open(dfn, "db.reopen");
+
+ // lookup at datum using immedate key
+ int dat = 232;
+ A_tblLoc::ikey_A_datum ky(db.a_tbl,dat);
+ chk( ky.Find(), "ky.Find()");
+ printf("Found %d name %s\n",dat,db.a_tbl.A_name());
+
+ // look at datum using relative key
+ int ret = 0;
+ A_tblLoc::rkey_A_datum rky(db.a_tbl);
+ if( !(ret=rky.First()) ) do {
+ printf("Data %d name %s\n",db.a_tbl.A_data(),db.a_tbl.A_name());
+ } while( !(ret=rky.Next()) );
+ if( ret == Db::errNotFound ) ret = 0;
+ chk( ret, "datam");
+
+ // get cursor position using immediate key
+ A_tblLoc::ikey_A_names iky(db.a_tbl,"b");
+ if( !(ret=iky.Locate()) ) {
+ // switch to relative key
+ A_tblLoc::rkey_A_names rky(db.a_tbl);
+ do {
+ printf("Name %s data %d\n",db.a_tbl.A_name(),db.a_tbl.A_data());
+ } while( !(ret=rky.Next()) );
+ }
+ if( ret == Db::errNotFound ) ret = 0;
+ chk( ret, "names");
+ chk( db.commit(1), "commit(1)");
+ db.close();
+ return 0;
+}
+
+
+Traveling db:
+only one process allowed write access, no readers allowed
+ all allowed read lock while not write locked
+to access database, typically:
+int resetDb(int rw)
+{
+ int result = 0;
+ if( access("/path/t.db", F_OK) ) db->create("/path/t.db");
+ if( !result ) result = openDb(rw);
+ return result;
+}
+int openDb(int rw) { return db->access("/path/tdb.db", SHM_KEY, rw); }
+void closeDb() { detachDb(); db->close(); }
+void commitDb() { db->commit(); } // detaches
+void undoDb() { db->undo(); } // detaches
+int attachDb(int rw) { return db->attach(rw); }
+int detachDb() { return db->detach(); }
+
+typical session:
+openDb(); detachDb(); ...
+//typical read access
+attachDb(); do_read(); detachDb(); ...
+//typical write access
+attachDb(1); do_write(); commitDb(); ...
+closeDb();
+