Patch title: Release 94 bulk changes
Abstract:
File: /pliant/storage/database/index.page
Key:
    Removed line
    Added line
title "Pliant database engine"


header "The Pliant database engine design"

  para
    [Pliant database model assumes that all your datas are stored in a tree.] ; eol
    [Each edge of the tree is labelled. ]
    [Some node of the tree (mainly the leaves) can be assigned a value.] ; eol
    [The engine provides functions in order to access and modify the datas both using direct access using the data path, and through a set of data types that enables you to map some part of the tree, then use it as it if was standard Pliant objects.] ; eol

  para
    [Pliant database model is not relational.]
   
  para
    [What it's good for (better than classical SQL engines):]
    list
      item [Flexibility: the way datas are truely stored is free, so the Pliant database engine can be used either as a true database engine, or as a front end to an SQL server, or even both at once. It could also handle more sophisticated data models where different records contain different sets of fields.]
      item [Reliably store a few thousands records (maybe with 1-n relations) handled through a light weighed engine (single ascii only file which means harder to corrupt, easily to read directly from disk by other applications or even humans using a text editor, and full recovery through processing the log).]
    [What it's also good for (just like ideal relational databases):]
    list
      item [Exporting datas: each data is identifyed very simply through it's path, which is embedded in HTML pages so that other computers can very easily extract them though parsing the <pdata> HTML tag.]
      item [Each data can be accessed through an URL.]
      item [Multi sites operations (even with weakly linked servers): synchronising through mixing the logs is trivial and efficient.]
    [What it's no good for (worse than classical SQL engines):]
    list
      item
        [Handle a large unsplited database. The problem here is not with the database model, but rather with the current implementation which stores all datas in the main memory of the computer since modern computer tend to provide a huge main memory that can store everything except very large databases. So, my first implementation choice was to prefer all in memory because it enabled me to provide a resonably efficient, and very very reliable, system with a fairly simple design.] ; eol
        [Also, you can resonably expect that contributors will provide classic binary on the disk alternatives in a few monthes or years that will be source compatible with all your applications, so I think my choice to prefer the model that can bring very fast a resonably efficient and very stable system, at the expense of a limit in database size, is wise.]
      item [Fast execute queries that need to scan a large set of datas: walking through the tree is not very fast, so if the query needs to review many millions of nodes, it will be slow. Again, the problem is not with the database model, but with the fact that the current implementation does not contain the Pliant optimizers that will pack and optimize multiple accesses.]
    [Conclusion:]
    list
      item [If your database is small, Pliant database engine should provide you a more flexible and reliable (when it will have been extensively reviewed and tested) system than classical SQL servers because it can provide resonable performences with a very simple implementation, and it's crashed proofed, so you can run both the application and the database server within the HTTP server, so avoid the communications overhead.]
      item [If you expect top performances on queries that require to scan a large number of datas, then a top quality SQL server (] ; link "MySQL" "http://www.mysql.com/" ;[) will behave much much better at the moment.]
      item [Also if your long queries are read only, you may decide to replicate the Pliant database to an SQL server that will be used in read only mode: you would then get the best of both worlds until Pliant database engine optimizer catches on.]

  para
    [About possible evolutions of this model over time:]
    list
      item
        [Pliant database engine model (the application side interface) has room for terrific speed improvements (without any change in the application) through using Pliant optimizer extension mecanism. Also, this will take many years to be best handled since it's not trivial, just like optimaly handling an SQL request is not trivial, and the resulting database engine will be fat, just like SQL servers are. ]
        [On the other hand, the current implementation is light, flexible ... and not top efficient. ]
      item
        [From a very general point of view about softwares history, Pliant database engine is a new (or very old) model (based on trees, or URLs if you prefer, whereas SQL relational databases model is tuples, or rows and columns if you prefer). ]
        [The new model is more abstract than the relational one, so an efficient disk implementation is less straight forward to provide, but the model is also more flexible, and better suited for weakly linked multi sites operations, so my bet is that these advantages will outperform the initial implementation troubles.]


header "Application level functions"

  para
    [This will explain the recommended interface to use in your applications.]

  para
    [This is mainly two steps:]
    list
      item [Declare the data types of your application.]
      item [Map your database in the general tree.]
      item [Accessing the datas.]

  header "Declaring the database data type"

    [You will use the usual 'type' Pliant function.] ; eol
    [The only database specific type is 'Set' generic data types that defines a table or a 1-n relation.]

    para
      [Here is an example:]
      listing
        module "/pliant/storage/database.pli"
        type Multi
          field Int k <- undefined
          field Str l
        type Test # this will be the records, with 'm' being a 1-n relation
          field Int i <- 5
          field Int j <- 10
          field Str ab <- "default"
          field Set:Multi m

    [You can find a real example in ] ; link "mail.pli" "/pliant/protocol/smtp/mail.pli" ; [ that defines the database for storing mailboxes configurations.] ; eol

  header "Mapping the database"

    [This is done using the 'load' method on 'Database' data type.] ; eol
    [The exact syntax is the following:]
    listing
      method `db ¤load `filename [¤log `logname] [¤mount `mountpoint]
        arg_rw Database `db ; arg Str `filename `logname `mountpoint
    list
      item
        [the 'filename' should end with] ; fixed [ .pdb ] ; [and specifies where the database is stored on the disk. It's an HTML like ascii file.] ; eol
      item
        [if specifyed, the 'logname' should end with] ; fixed [ .log ] ; [and specifies where all changes in the database should be logged. An interesting property is that you can delete the .pdb file and replace it with the .log file: it will build the same database, so it can be used to rewind some specific changes.] ; eol
      item
        [if specifyed, the 'mountpoint' will map the database in the general database tree. The mountpoint should look like /] ; italic [your_orgnanisation_name] ; [/] ; italic [your_application] ; [ in order to avoid namespace clashes. If you plan to access your datas using the HTTP server forms, you have to mount it so that it can assign a unique name (the data path) to each data in your database.]

    para
      [This is continuing the previous example:]
      listing
        gvar (Database Set:Test) db
        db load "file:/tmp/test.pdb"

  header "Accessing the datas"
 
    [The good news is that now, you can access datas in your database as easily as if the database was a standard Pliant global variable. ]
    [In a multithreaded environment such as the HTTP server, your even don't have to care about locking since Pliant engine will do it for you and grant that any data you access is a real one so it wont crash the process. ]
    [Of course, you are free to add application level locking whenever you want to grant that a data is not changed between the time you read it and the time you write it back, and so on.] ; eol

    para
      [These are the features specific to the database engine:]
      list
        item
          [You must not access a database data using 'Pointer' or 'Link' generic pointers, but rather 'Data' generic pointer. ]
          link "see sample below" "" section "pointer sample" ; [.]
        item
          [When you have a pointer to a data, 'keyof' function will provide you the name of the edge in the tree leading to the data, and 'pathof' will provide the all path leading to the data. ]
          link "see sample below" "" section "key sample" ; [.] ; eol
        
    para
      [This is continuing the inline example:]
      listing
        console db:"abc":i eol
      [Displays the 'i' field in the record with 'abc' key.] ; eol
      [If the record does not exist, it's not an error: the default value for 'i' will be returned.]       
      section "key sample"
      listing
        console (keyof db:"abc":i) eol
      [Displays the key associated with the 'i' field, so it will always be 'i'.]
      listing      
        console (pathof db:"abc":i) eol
      [Displays the path associated with the 'i' field in the record with 'abc' key, so it will be '/abc/i'.]
      listing      
        console (pathof db:"abc,def":i) eol
      [Displays the path associated with the 'i' field in the record with 'abc,def' key, so it will be '/abc&#44;def/i'.] ; eol
      [This demonstrates that each element in the path is HTML encoded in order to avoid any clash between '/' in a key and '/' as the keys separator, and any characters encoding problem in files.]
      listing
        db:"abc":i := 3
      [Sets the 'i' field.] ; eol
      [If the record with 'abc' key does not exists, the instruction will be ignored, and it's not an error.]
      section "pointer sample"
      listing
        var Data:Test p :> db "abc"
        console p:i eol
      [Displays once more the 'i' field in the record with 'abc' key.] ; eol
      [It's very important to notice that you most not use 'Pointer:Test' or 'Link:Test' but rather 'Data:Test'.]
      listing
        each r db:"abc":m
          console r:k eol
      [Enumerates all records in the 1-n relation 'm' of the record associated with 'abc' key.]
      small
        eol ; [Please notice that 'each' meta creates a temporary variable, 'r' in this example, so can be used only in a function.]
      listing
        each r db:"abc":m filter r:l<>"" sort r:k^2
          console r:k eol
      [Same, but a filter will be first applyed that will select only records that match the condition, then these records will be sorted according to the specifyed formula.]
      listing
        db:"abc":m create "u"
      [Creates the record associated with 'u' key, if it does not already exists.]
      listing
        db:"abc":m delete "u"
      [Deletes the record associated with 'u' key, if it exists.]
      listing
        data_reset db:"abc"
      [Resets the record with 'abc' key to it's default value, so all fields will be set to the default value, and all records in 1-n relation will be discarded.]

    [If you looked at the real ] ; link "mail.pli" "/pliant/admin/mail.pli" ; [ sample earlier, then you should now look at ] ; link "mail.page" "/pliant/admin/mail.page" ; [ that provides an access to this database though a web browser.]

header "Implementation details and low level interface"

  [If you use this low level interface described below, you have to care with locking, and it's not that easy, so this is provided rather as an overview of the engine design, not a recommended interface for your application.] ; eol
  [Also, there is one sample program dealing with the low level database interface: the Pliant ] ; link "datas browser" "/pliant/browse/data/virtual_tree.page" ; [ in the HTTP server.]

  header "Database engine modules"

    table columns 2
      cell
        link "prototype.pli" "prototype.pli"
      cell
        [Defines the various data types and the generic methods they use. So, you can see it as the API of the low level database engine.] ; eol
        [You can extend the Pliant database engine through creating new data types that implement these methods. An example would be a set of types that implement handling datas stored on the disk, just like classical database engines.]
      cell
        link "inmemory.pli" "inmemory.pli"
      cell
        [In the provided implementation of the Pliant database engine, all datas are stored in the main memory.] ; eol
        [So, simple types such as 'Int' or 'Str' will be handled by ] ; link "DataField" "inmemory.pli" section "type DataField" ; [ interface, ]
        [multi fields objects will be handled by ] ; link "DataRecord" "inmemory.pli" section "type DataRedord" ; [ interface, ]
        [and 'Set' generic types will be handled by ] ; link "DataTable" "inmemory.pli" section "type DataTable" ; [ interface.]
      cell
        link "file.pli" "file.pli"
      cell
        [In the provided implementation of the Pliant database engine, all databases are stored in HTML like ascii files.] ; eol
        [These are implemented as ] ; link "DatabaseFile" "file.pli" section "type DatabaseFile"
      cell
        link "mount.pli" "mount.pli"
      cell
        [In order to be usable straight forward in HTTP server forms, the databases have to be mounted in a single tree. This tree is implemented as ] ; link "DataMount" "mount.pli" section "type DataMount" ; [ interface.]
      cell
        link "set.pli" "set.pli"
      cell
        [Implements the ] ; link "Set" "set.pli" section "type Set" ; [ generic data type that is used to define tables or 1-n relations in databases.]
      cell
        link "pointer.pli" "pointer.pli"
      cell
        [This module implements the high level interface, witch consists mainly of ] ; link "Data" "pointer.pli" section "type Data" ; [ and ]  ; link "Database" "pointer.pli" section "type Database" ; [ generic data types.]

  header "Low level interface"

    para
      [The low level interface to deal with the data tree nodes is defined in ] ; link "prototype.pli" "prototype.pli" ; [. ]
      [There are only 9 methods to deal with the data nodes and walk the tree. ]
      table columns 2
        cell
          []
        cell header
          link "DataInterface_" "prototype.pli" section "type DataInterface_" ; [ generic methods]
        cell
          [type ] ; how "prototype.pli" section "method type"
        cell
          [queries the type of the current value of the node. This is only informative since you can read or write any type. Casting will append whenever required (casting is always done through converting to a string, then parsing the string).]
        cell
          [get ] ; how "prototype.pli" section "method get"
        cell
          [read the content of the node.]
        cell
          [set ] ; how "prototype.pli" section "method set"
        cell
          [writes the content of the node.]
        cell
          [reset ] ; how "prototype.pli" section "method reset"
        cell
          [resets the content of the subtree to it's initial value.]

        cell
          [search ] ; how "prototype.pli" section "method search"
        cell
          [returns a pointer to the son with the specifyed key.]
        cell
          [first ] ; how "prototype.pli" section "method first"
        cell
          [gets the first son (that has a key within the specifyed range of values).]
        cell
          [next ] ; how "prototype.pli" section "method next"
        cell
          [gets the next one]
        cell
          [count ] ; how "prototype.pli" section "method count"
        cell
          [how many sons are there (that has a key within the specifyed range of values) ?]
        cell
          [create ] ; how "prototype.pli" section "method create"
        cell
          [creates a new edge (creates a new record in a table or a 1-n relation).]
        cell
          [delete ] ; how "prototype.pli" section "method delete"
        cell
          [cuts an edge (removes a record in a table or a 1-n relation).]


  header "Low level implementation"

    para
      [The straight forward implementation whould be to store each database data (or node, it's the same) in a Pliant object, and require this object to implement the generic methods we just listed. ]
      [Also, this would lead to poor performances since a node containing a very small data (let's say an 'Int') whould be too fat, so the real implementation is a bit more tricky, but performs much better:] ; eol
      [rather than storing all informations in the data node, we store as much as possible in the pointer to the data. ]
    para
      [So, A Pliant database data is an arbitrary Pliantobject, or even a field in a Pliant object,] ; eol
      [and pointer to a database data is type ] ; link "Data_" "prototype.pli" section "type Data_" ; [ and contains the following informations:]
      list
        item
          fixed [adr ] ; [the address of the real database data (or node, it's the same).]
        item
          fixed [object ] ; [an arrow to the objet the real data belongs to. You can think about 'adr' beeing the address of the field and 'object' beeing the address of the record. We need the arrow to the record in order to prevent the object to be freed by another object as long as we are mapping it.]
        item
          fixed [interface ] ; [as specifyed arealier in this document, a data in Pliant database is not a true Pliant object, because it would be too memory hungry for small objects such as 'Int', so it cannot have generic methods. ]
          [So, this is the link to the true object, which must implement the set of generic methods for type ] ; link "DataInterface_" "prototype.pli" section "type DataInterface_" ; [ listed earlier in this document.]
        item
          fixed [base ] ; [each node in Pliant database belongs to a Pliant database. A Pliant database is a true Pliant object that implements the generic methods] ; how "prototype.pli" section "method get_root" ; [ and fields] ;how "prototype.pli" section "type Database_" ;[ defined in ] ; link "Database_" "prototype.pli" section "type Database_" ; [ data type. ]
          [Locking and changes loggin appends at database level.]
        item
          fixed [path1 ] ; [and] ; fixed [ path2 ] ; [store the data node path. We could have a single 'path' field with type 'Str', but the povided implementation is more efficient. You can use 'path'] ; how "prototype.pli" section "method path" ; [ method to get the data path as a standard string, or 'key'] ; how "prototype.pli" section "method key" ; [ to get the name of the last edge in the path leading to the current data.]
  

  header "Low level functions"

    listing
      gvar Data_ data_root
    [The database pointer to the root node in the database tree.]
    
    listing
      method d1 search_path path createit -> d2
        arg Data_ d1 ; arg Str path ; arg CBool createit ; arg Data_ d2
    [Gets a database pointer to a subnode in the database] ; how "pointer.pli" section "method search_path"

  header "Implementation status"
  
    [The current status of the database engine is described in the ]
    link "database engine" "/pliant/welcome/project/database"
    [ section of the ]
    link "Pliant overall project" "/pliant/welcome/project/"
    [.]