The Sql_orm package provides an "Object-Relational Mapper" interface between a Sqlite3 database and OCaml. DESCRIPTION ----------- You provide a schema of the type of objects you want to store in the database, and an OCaml source file is generated with modules that: * initialize the database with the right schema * retrieve objects from the database by searching from keys * modify fields and save them back to the database. The schema provides the following types: * text: a standard text field * blob: binary data * date: date stored as seconds since 1970 * integer: int64 * real: floating point number * foreign: map another object reference * foreign_many: list of other object references You can also specify flags for each field: * `Optional: the field can be NULL in the database and is represented as an OCaml option type. * `Index: generate an index on the key's table for faster lookups. * `Unique: enforce a uniqueness constraint on the field. For every table, you can also specify global table options: * A list of field names which specify unique constraints for groups of fields. This is represented as a UNIQUE INDEX in the SQL backend. The OCaml module which is generated contains a generic retrieval function which can be used for most queries. However, if you know in advance that you only need certain fields retrieved, or a single filtering criteria, you can request additional functions to be generated by the ORM layer. For example, consider the schema in examples/addressbook/schema.ml: "name": text (unique) "age": integer (optional) "email": text When the schema generator is executed, it creates an OCaml module and signature. In our example, we create My_db which is in examples/addressbook/my_db.ml(i). The default OCaml generated retrieval function has signature: val get : ?id:int64 option -> ?name:string option -> ?age:int64 option -> ?email:string option -> ?custom_where:string * Sqlite3.Data.t list -> Init.t -> t list All the search fields default to None, which means simply calling Person.get will result in all the records in the database being retrieved. If you specify a value for a field, it is included as a WHERE constraint in the database query. The "custom_where" is an advanced option to include more complex SQL constraints such as LIKE clauses. Now, suppose you are only interested in looking up people's names of a certain age? You could specify a generic query: let qs = Person.get ~age:(Some 30L) db in List.map (fun q -> q#name) qs Instead of going through this step, when generating the schema you can specify additional static query functions: [ ["name"] , ["age"] ] The first element in the tuple is the list of fields to retrieve, and the second element represents the constraint fields. This would generate a function: val get_name_by_age : age:int64 option -> ?custom_where:string * Sqlite3.Data.t list -> Init.t -> string list This signature is much simpler; you just pass in the single field you want to filter on (age), and instead of a full (Person.t list) you get back a list of name strings instead. The SQL query is also correspondingly more efficient since it doesnt have to SELECT unnecessary fields as well. INSTALLATION ------------ To get started, compile and install the Sql_orm module with: $ make && sudo make install It uses Findlib to locate your OCaml installation. You will also need the OCaml-SQLite3 bindings by Markus Mottl, available from: * http://www.ocaml.info/home/ocaml_sources.html USAGE ----- Once the library is installed, take a look inside the examples/ and tests/ directory for how to use it. The test_schema.ml file defines a simple address book schema and calls into the Sql_orm generator to output OCaml code. If you execute run_tests.sh in that directory, it will generate the code into ormtest.ml (and ormtest.mli), and execute some test cases defined in test.ml. Once the tests have (hopefully successfully) run, you can examine the database schema in test.db to see how it maps from the schema you defined to SQLite3. In the generated file, each schema object you define has its own module, with a 't' function to instantiate a new one, and 'get' to retrieve a list of objects from the database. Labelled arguments corresponding to the fields are used with native OCaml types, and are mapped to the correct SQLite types. BUGS ---- Please send bug reports or patches to: * Anil Madhavapeddy <anil@recoil.org>