NAME

WeBWorK::DB - interface with the WeBWorK databases.

SYNOPSIS

my $db = WeBWorK::DB->new($dbLayout);

my @userIDs = $db->listUsers();
my $Sam = $db->{user}->{record}->new();

$Sam->user_id("sammy");
$Sam->first_name("Sam");
$Sam->last_name("Hathaway");
# etc.

$db->addUser($User);
my $Dennis = $db->getUser("dennis");
$Dennis->status("C");
$db->putUser->($Dennis);

$db->deleteUser("sammy");

DESCRIPTION

WeBWorK::DB provides a consistent interface to a number of database backends. Access and modification functions are provided for each logical table used by the webwork system. The particular backend ("schema" and "driver"), record class, data source, and additional parameters are specified by the hash referenced by $dbLayout, usually taken from the course environment.

ARCHITECTURE

The new database system uses a three-tier architecture to insulate each layer from the adjacent layers.

Top Layer: DB

The top layer of the architecture is the DB module. It provides the methods listed below, and uses schema modules (via tables) to implement those methods.

/ new* list* exists* add* get* get*s put* delete* \          <- api
+------------------------------------------------------------------+
|                                DB                                |
+------------------------------------------------------------------+
\ password permission key user set set_user problem problem_user /  <- tables

Middle Layer: Schemas

The middle layer of the architecture is provided by one or more schema modules. They are called "schema" modules because they control the structure of the data for a table.

The schema modules provide an API that matches the requirements of the DB layer, on a per-table basis. Each schema module has a style that determines which drivers it can interface with. For example, SQL is an "dbi" style schema.

Bottom Layer: Drivers

Driver modules implement a style for a schema. They provide physical access to a data source containing the data for a table. The style of a driver determines what methods it provides. All drivers provide connect(MODE) and disconnect() methods. A dbi style driver provides a handle() method which returns the DBI handle.

Record Types

In %dblayout, each table is assigned a record class, used for passing complete records to and from the database. The default record classes are subclasses of the WeBWorK::DB::Record class, and are named as follows: User, Password, PermissionLevel, Key, Set, UserSet, Problem, UserProblem. In the following documentation, a reference the the record class for a table means the record class currently defined for that table in %dbLayout.

These exceptions will replace the ones in WeBWorK::DB::Schema and will be allowed to propagate out to calling code. The following callers will have to be changed to catch these exceptions instead of doing string matching:

lib/WebworkSOAP.pm: if ($@ =~ m/user set exists/) { lib/WeBWorK/ContentGenerator/Instructor.pm: if ($@ =~ m/user set exists/) { lib/WeBWorK/ContentGenerator/Instructor.pm: if ( $@ =~ m/user set exists/ ) { lib/WeBWorK/ContentGenerator/Instructor.pm: if ($@ =~ m/user problem exists/) { lib/WeBWorK/ContentGenerator/Instructor.pm: if ($@ =~ m/user problem exists/) { lib/WeBWorK/ContentGenerator/Instructor.pm: next if $@ =~ m/user set exists/; lib/WeBWorK/Utils/DBImportExport.pm: if ($@ =~ m/exists/) { lib/WeBWorK/DB.pm: if ($@ and $@ !~ m/password exists/) { lib/WeBWorK/DB.pm: if ($@ and $@ !~ m/permission level exists/) {

How these exceptions should be used:

* RecordExists is thrown by the DBI error handler (handle_error in Schema::NewSQL::Std) when in INSERT fails because a record exists. Thus it can be thrown via addUser, addPassword, etc.

* RecordNotFound should be thrown when we try to UPDATE and zero rows were affected. Problem: Frank Wolfs (UofR PAS) may have a MySQL server that returns 0 when updating even when a record was modified. What's up with that? There's some question as to where we should throw this: in this file's put* methods? In Std.pm's put method? Or in update_fields and update_fields_i?

* DependencyNotFound should be throws when we check for a record that is needed to insert another record (e.g. password depends on user). These checks are done in this file, so we'll throw this exception from there.

CONSTRUCTOR

new($dbLayout)

The new method creates a DB object and brings up the underlying schema/driver structure according to the hash referenced by $dbLayout.

$dbLayout Format

$dbLayout is a hash reference consisting of items keyed by table names. The value of each item is a reference to a hash containing the following items:

record

The name of a perl module to use for representing the data in a record.

schema

The name of a perl module to use for access to the table.

driver

The name of a perl module to use for access to the data source.

source

The location of the data source that should be used by the driver module. Depending on the driver, this may be a path, a url, or a DBI spec.

params

A reference to a hash containing extra information needed by the schema. Some schemas require parameters, some do not. Consult the documentation for the schema in question.

For each table defined in $dbLayout, new loads the record, schema, and driver modules. It the schema module's tables method lists the current table (or contains the string "*") and the output of the schema and driver modules' style methods match, the table is installed. Otherwise, an exception is thrown.