Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / db / doc
diff --git a/cinelerra-5.1/db/doc b/cinelerra-5.1/db/doc
new file mode 100644 (file)
index 0000000..68f428a
--- /dev/null
@@ -0,0 +1,364 @@
+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();
+