Yggdrasil Content Managment System

No ePatents !
Foundation for a Free Information Infrastructure

Nidhogg Database Abstract Layer Readme


                   Nidhogg Database Abstract Layer


=== 1. Introduction
=== 2. Installation
=== 3. Quick tutorial
=== 3.1. Getting started
=== 3.2. Querying and fetching results
=== 3.3. Identifiers
=== 3.4. Transactions
=== 3.5. Information and statistics
=== 3.6. Error handling
=== 4. API
=== 4.1. Constants
=== 4.1.1. Transaction
=== 4.1.2. Capabilities
=== 4.2. NDBAL Class
=== 4.2.1. checkDriverName() method
=== 4.2.2. close() method
=== 4.2.3. createInstance() method
=== 4.2.4. defaultErrorHandler() method
=== 4.2.5. endTransaction() method
=== 4.2.6. getCapabilities() method
=== 4.2.7. getDBInfo method
=== 4.2.8. getDriverInfo() method
=== 4.2.9. getDatabaseInfo() method
=== 4.2.10. getError() method
=== 4.2.11. getErrorHandler() method
=== 4.2.12. getGlobalErrorHandler() method
=== 4.2.13. getGlobalStats() method
=== 4.2.14. getLastID() method
=== 4.2.15. getLastQuery() method
=== 4.2.16. getLastResult() method
=== 4.2.17. getNextID() method
=== 4.2.18. getNextIDs() method
=== 4.2.19. getStats() method
=== 4.2.20. getTableMap() method
=== 4.2.21. getTablePrefix() method
=== 4.2.22. getTransactionMode() method
=== 4.2.23. isCapableOf() method
=== 4.2.24. isCapableOfOneOf() method
=== 4.2.25. isDriverLoaded() method
=== 4.2.26. loadDriver() method
=== 4.2.27. mapTable() method
=== 4.2.28. open() method
=== 4.2.29. opened() method
=== 4.2.30. parseURL() method
=== 4.2.31. phpExt() method
=== 4.2.32. ping() method
=== 4.2.33. query() method
=== 4.2.34. queryf() method
=== 4.2.35. setDefaultErrorHandlerOutput() method
=== 4.2.36. setErrorHandler() method
=== 4.2.37. setGlobalErrorHandler() method
=== 4.2.38. squeryf() method
=== 4.2.39. startTransaction() method
=== 4.2.40. uquery() method
=== 4.2.41. uqueryf() method
=== 4.3. NDBALResult Class
=== 4.3.1. countRows() method
=== 4.3.2. countAffectedRows() method
=== 4.3.3. fetch() method
=== 4.3.4. fetchObject method
=== 4.3.5. fetchRow() method
=== 4.3.6. fetchRest() method
=== 4.3.7. fetchAll() method
=== 4.3.8. free() method
=== 4.3.9. freed() method
=== 4.3.10. getCapabilities() method
=== 4.3.11. getDB() method
=== 4.3.12. getQueryString() method
=== 4.3.13. isCapableOf() method
=== 4.3.14. isCapableOfOneOf() method
=== 4.3.15. seek() method
=== 4.4. Other
=== 4.4.1. is_a() function
=== 5. Drivers
=== 5.1. PostgreSQL Driver
=== 5.2. MySQL Driver
=== 5.3. SQLite Driver
=== 5.4. Null Driver
=== 5.5. Log Driver
=== 5.5.1. The API
=== 5.5.1.1. getDatabase() method
=== 5.6. MultiDB Driver
=== 5.6.1. How does it work
=== 5.6.2. Configuration
=== 5.6.3. The API
=== 5.6.3.1. countDatabases() method
=== 5.6.3.2. getCurrentDatabase() method
=== 5.6.3.3. getDatabase() method
=== 5.6.3.4. getDatabaseByTable() method
=== 5.6.3.5. getTableCheckCallback() method
=== 5.6.3.6. selectDatabase() method
=== 5.6.3.7. selectDatabaseByTable() method
=== 5.6.3.8. setTableCheckCallback() method
=== 5.6.3.9. lockDatabaseChanging() method
=== 5.7. Specifying a real driver
=== 6. Writing a driver
=== 6.1. PHP5 vs PHP4 code
=== 6.2. Notes about constructors
=== 6.3. The main class
=== 6.3.1. The constructor
=== 6.3.2. _close() method  [required]
=== 6.3.3. _endTransaction() method
=== 6.3.4. _getError() method  [required]
=== 6.3.5. _getLastID() method
=== 6.3.6. _getNextID() method
=== 6.3.7. _getNextIDs() method
=== 6.3.8. _open() method  [required]
=== 6.3.9. _ping() method
=== 6.3.10. _query() method  [required]
=== 6.3.11. _startTranasction()
=== 6.4. The result class
=== 6.4.1. The constructor
=== 6.4.2. _countAffectedRows() method
=== 6.4.3. _countRows() method
=== 6.4.4. _fetch() method  [required]
=== 6.4.5. _free() method
=== 6.4.6. _seek() method
=== 7. Copyright
=== 7.1. Library
=== 7.2. Documentation
=== 7.3. GNU Lesser General Public License
=== 7.4. GNU Free Documentation License


                             Documentation is like sex:
                             when it is good, it is very, very good; and
                             when it is bad, it is better than nothing.
                               -- Dick Brandon


=== 1. Introduction

The Nidhogg Database Abstract Layer is a library which provides a
database independent software package to developers. Scripts written
with this library can be easily ported to other environments with
different database engines... or at least that's the idea.. ;)

NDBAL is part of the Yggdrasil Content Management System project whose
homepage can be found at: <URL:http://yggdrasil-cms.sourceforge.net/>.
You can also view a project summary page at SourceForge.NET
<URL:http://sourceforge.net/projects/yggdrasil-cms/>.  If you have
questions check one of those sites... if you can't find an answer then
you'll still find an email address!


=== 2. Installation

To install the library copy all the files from the  ndbal  directory
to the same place.  For example, if your script file is located in
/home/zenon/public_www/  and all the files which you include in that
script are in  /home/zenon/public_www/includes/  then copy all the
NDBAL files to that include directory:

	$ cp ndbal/* /home/zenon/public_www/includes/


=== 3. Quick tutorial

This section will show how to use the NDBAL library. It won't cover
all aspects of the library, so please refer to the API section if you
need further information.


===== 3.1. Getting started

To use the library in a PHP script you must include the  ndbal.inc.php
file. The file requires a constant named  IN_PHP_SCRIPT  to be defined
before it is included so that it can make sure it wasn't accessed
directly by a user through the web browser. The code to include the
library could look as follows:

	defined('IN_PHP_SCRIPT') or define('IN_PHP_SCRIPT', true);
	require_once('./includes/ndbal/ndbal.inc.php');

replacing the path to the library with the appropriate location on
your system.

After you've included the library you can connect to the
database. First you have to create an instance of the driver class;
the simplest way to achieve this is to use the  createInstance()
method. The method returns either an instance of the  NDBAL  object or
an error message; the  is_string()  function is used to check for
errors. For example:

	$conf = array();
	$conf['driver'] = 'pgsql';
	$conf['host'] = 'db.example.com';
	$conf['user'] = 'zenon';
	$conf['password'] = '*****';  // ^_^
	$conf['prefix'] = 'site_';
	$DB = &NDBAL::createInstance($conf);
	if (is_string($DB)) {
	    // Failed
	    die("Error creating driver's instance: $DB");
	}
	// Everything's OK..

Since version 1.1.0.2 you can alternatively use an URL to create an
instance of the driver. Generally the URL will look something like the
following:

	driver://user:password@host:port/dbname?other#options    (1)
	driver://user:password@[host]:port/dbname?other#options  (2)
	driver://user:password@[:socket]/dbname?other#options    (3)

You are only required to specify the driver section. The first
character of the socket section in format 3 can't be a colon because
it would be interpreted as an IPv6 address; format 2 should be used
with IPv6 addresses. It follows then that the host section in formats
1 and 2 can't begin with a colon because it would then be interpreted
as a socket. Using these URLs, the above example would be written as
follows:

	$url = 'pgsql://zenon:*****@db.example.com?prefix=site_';
	$DB = &NDBAL::createInstance($url);
	if (is_string($DB)) {
	    // Failed ;/
	    die("Error creating driver's instance: $DB");
	}
	// Every thing's OK..

Both methods can be combined, which is especially useful if you have
many options to set, or if some options are not strings:

	$url = 'pgsql://zenon:*****@db.example.com';
	$conf = array(
	    'prefix'  => 'site_',
	    'map.foo' => 'bar',
	    'map.bar' => 'foo',
	);
	$DB = &NDBAL::createInstanceFromURL($url, $conf);
	if (is_string($DB)) {
	    // Failed
	    die("Error creating driver's instance: $DB");
	}
	// Every thing's OK..

Once you have created a new instance of the driver class you can open
a connection:

	if (!$DB->open()) {
	    die('Connection failed.');
	}


===== 3.2. Querying and fetching results

Traditional methods of executing queries may cause a SQL injection, so
NDBAL provides another method; a combination of the usual methods with
a  printf()  function. This method decreases risk of a SQL injection;
for example:

	$result = &$DB->queryf("SELECT * FROM %t WHERE user_posts>%d",
	                       'users', $_GET['posts']);
	if (!$result) {
	    die('Query failed.');
	}

In some cases, using an unbuffered query might be better. To do this
just use the  uqueryf()  method instead of  queryf() . However,
remember that: (a) unbuffered queries don't support the  countRows()
method (b) the driver may be incapable of performing unbuffered
queries, in which case it will ignore your request for an unbuffered
query and run a normal query instead.

Once the query is successfully executed a new instance of  NDBALResult
is created, which can be used to fetch the results. The  fetch()
method will obtain any rows returned. If you want to know in advance
how many rows have been returned you should use the  countRows()
method (note however that this method may be unsupported by a
particular driver). For example:

	$result = &$DB->queryf("SELECT user_name FROM %t", 'users');
	if ($DB->isCapableOf(NDBAL_C_NUM_ROWS)) {
	    echo('Number of users: ' . $result->countRows() . "n");
	}
	$i = 0;
	while (($user = $result->fetch())) {
	    printf("%3d: %sn", ++$i, $user['user_name']);
	}

To fetch all rows and place them into an array you can use the
fetchRest()  method; however this method must be used before fetching
anything else:

	$result = &$DB->queryf("SELECT user_name FROM %t", 'users');
	$users = $result->fetchRest();

If a result is capable of  NDBAL_C_SEEK , you can use the  seek()  and
fetchAll()  methods along with an index argument to the  fetch()
method. Those methods could be used to go through the results set
multiple times:

	$result = &$DB->query("something");
	if (!$result) die('Query failed');
	if (!$result->isCapableOf(NDBAL_C_SEEK)) {
	    die('Required capability missing');
	}

	// 1st pass
	while (($row = $result->fetch())) {
	    // Do something
	}

	// 2nd pass
	$result->seek(); // 0 is default argument
	while (($row = $result->fetch())) {
	    // Do something
	}

To check how many rows have been affected after the last
INSERT/UPDATE/DELETE query use the  countAffectedRows()  method
(although some drivers may not support it):

	$result = &$DB->queryf("DELETE FROM %t WHERE user_donate < %d",
	                       'users', 5);
	if ($this->isCapableOf(NDBAL_C_AFFECTED_ROWS)) {
	    echo('Deleted ' . $result->countAffectedRows() .
	         ' record(s).');
	}


===== 3.3. Identifiers

It is sometimes important to know the ID of the last row inserted so
that it can be used as reference in subsequent INSERT queries. The
NDBAL library contains three methods which can be used for this
purpose:  getLastID() , getNextID()  and  getNextIDs() . getLastID()
returns the ID of the last row inserted, while  getNextID()  returns a
unique ID which can be used in future INSERT queries. getNextIDs()
allows you to get multiple unique IDs at the same time.

It is very likely that a driver will support either none or only one
of those methods, so your code shouldn't rely on either of them but be
versatile enough to use both, depending on which is supported. A
method (which has already been used in this tutorial) to check
compatibility, called  isCapableOfOneOf()  is provided, along with two
constants as arguments. For example:

	// Check if we are able to get ID
	if (!$DB->isCapableOfOneOf(NDBAL_C_ANY_LAST_ID | NDBAL_C_NEXT_ID)) {
	    die('Could not insert data.');
	}

	// Get IDs
	if ($DB->isCapableOf(NDBAL_C_NEXT_ID)) {
	    $ids = $DB->getNextIDs('topics.topics_id as tid, ' .
	                           'posts.post_id as pid');
	    if (!$ids && !$DB->isCapableOfOneOf(NDBAL_C_ANY_LAST_ID)) {
	        die('Error.');
	    }
	} else {
	    $ids = false;
	}

	// Insert topic
	$DB->queryf("INSERT INTO %t (topic_id, topic_title),
	             VALUES (" . ($ids?$ids['tic']:'null') . ",
	                     '%s')", 'topics', $title);
	$tic = $ids?$ids['tic']:$DB->getLastID('topics', 'topic_id');

	// Insert post
	$DB->queryf("INSERT INTO %t (post_id, topic_id, post_title,
	                             user_id),
	             VALUES (" . ($ids?$ids['pic']:'null') . ",
	                     %d, '%s', %d)", 'posts', $tic, $title,
	            $poster_id);
	$pic = $ids?$ids['pic']:$DB->getLastID('posts', 'post_id');

	// Insert post data
	$DB->queryf("INSERT INTO %t (post_id, post_data)
	             VALUES (%d, '%s')", 'posts_data', $pid, $message);


===== 3.4. Transactions

Transactions can be controlled using the  startTransaction()  and
endTransaction()  methods; however certain drivers may be not capable
of transactions. To check whether a transaction has been started you
should use the  getTransactionMode()  method.

If the driver is not capable of transactions you can still use the
startTransaction()  method to enable auto ignore mode, which will
cause all queries after the first failed query to be ignored. They
will continue to be ignored until this mode is disabled using
endTransaction() .

	// Start transaction and auto ignore mode
	$DB->startTransaction(true);

	// Do some stuff...

	if ($everything_went_ok) {
	    // Commit
	    $DB->endTransaction(true);
	} else {
	    // Rollback
	    $DB->endTransaction(false);
	}


===== 3.5. Information and statistics

getLibInfo() , getDriverInfo() , getDBInfo() , getStats()  and
getGlobalStats()  methods are provided for gathering various
information about the library, the driver and the database.
 - getLibInfo()     -- returns information about the NDBAL library
                       (revision, name, author) (static method)
 - getDriverInfo()  -- returns information about the driver (name,
                       codename, version) (static method)
 - getDBInfo()      -- returns information about the database to which
                       you are connected
 - getStats()       -- returns query statistics on queries executed
                       using a particular driver
 - getGlobalStats() -- returns query statistics on queries executed
                       using all drivers
Those methods can be used as follows:

	// Info
	printf('Connected to %s server version %s on %s:%s using ' .
	       '%s version %s by <a href="mailto:%s">%s</a> and ' .
	       '%s library version %s by %s', $DB->getDBInfo('name'),
	       $DB->getDBInfo('version', 'N/A'),
	       $DB->getDBInfo('host', 'N/A'), $DB->getDBInfo('port', 'N/A'),
	       $DB->getDriverInfo('name'),
	       $DB->getDriverInfo('version', 'N/A'),
	       $DB->getDriverInfo('email', 'N/A'),
	       $DB->getDriverInfo('author', 'N/A'),
	       NDBAL::getLibInfo('name'),
	       NDBAL::getLibInfo('version', 'N/A'),
	       NDBAL::getLibInfo('author'));
	// Note that it is likely you will end up with a lot of 'N/A's

	// Number of queries
	$info = NDBAL::getGlobalStats();
	if ($info['queries']==0) {
	    echo('No queries were executed.');
	} elseif ($info['queries']==1) {
	    echo('1 query was executed (and it ' .
	         ($info['failed']=='0'?'succeed':'failed') . ').');
	} else {
	    echo($info['queries'] . ' queries were executed (' .
	         ($info['failed']==0?'all succeeded':
	         "${info['failed']} failed") . ').');
	}


===== 3.6. Error handling

In NDBAL 1.1.0.2 a new error handling mechanism was introduced which
was then changed in NDBAL 1.1.1. It now allows you to
log/print/whatever just after the error (or generally an event)
occured. Not much else to say about it ;) so just a little example:

	function my_error_handler(&$caller, $type, $message, $arg) {
	    $message = "$type: NDBAL message: $message";

	    // Database error
	    $err = $caller->getError();
	    if ($err && $err!==$message) {
	        $message .= "; Database error: $err";
	    }

	    // Print
	    echo("$messagen");
	}

	$DB->errorHandler('my_error_handler');
	$DB->query('DUMMY QUERY');  // Error occures here

See API description of  NDBAL::setGlobalErrorHandler()  and
NDBAL::defaultErrorHandler()  for more info.


=== 4. API


===== 4.1. Constants


======= 4.1.1. Transaction

There are several constants used to check what transaction mode the
driver is currently in. Those are: NDBAL_TM_NO, NDBAL_TM_TRANSACTION,
NDBAL_TM_AUTO_IGNORE and NDBAL_TM_AUTO_IGNORE_TRANSACTION. See the
description of the  NDBAL::getTransactionMode()  method for more
details.


======= 4.1.2. Capabilities

There are several constants representing a driver's capabilities:
NDBAL_C_NUM_ROWS, NDBAL_C_AFFECTED_ROWS, NDBAL_C_TRANSACTION,
NDBAL_C_PING, NDBAL_C_LAST_ID, NDBAL_C_NEXT_ID, NDBAL_C_SEEK,
NDBAL_C_LAST_ID_WITH_NAME and NDBAL_C_BUFFERED. See the description of
the  NDBAL::isCapableOf()  method for details.

Please note, that in this documetnation those constants' names will be
used as a names of actions which they correspond to therefore a
structure like "driver is capable of NDBAL_C_TRANSACTION" will mean
"driver is capable of menaging transactions" and so on.


===== 4.2. NDBAL Class

This is the main class. It can be used to gather information about the
NDBAL library and to make connections. After a connection has been
established you can then use an instance of this class to execute
queries and perform operations on a database, including gathering
information.


======= 4.2.1. checkDriverName() method

	static bool checkDriverName(string $name)

Returns  true  if the specified driver name is valid (ie. contains only
letters and digits), false otherwise.


======= 4.2.2. close() method

	bool close()

Closes the database connection and returns  true .

If a connection has not been opened, or if close has already been
called (see NDBAL::opened() ) then this method will return  true  and
do nothing.


======= 4.2.3. createInstance() method

	static mixed createInstance(string $url)
	static mixed createInstance(array $conf, string $prefix = '')
	static mixed createInstance(string $url, array $conf,
	                            string $prefix = '',
	                            bool $overwrite_url = false)

Loads a driver if required and creates new instance of the driver.

The configuration is taken from either  $url  (see  NDBAL::parseURL()
),  $conf  or both. If an option is available in both $url and  $conf
the value taken depends on  $overwrite_url . If it is  false then the
value is taken from  $url ; otherwise it is taken from  $conf .

The  $conf  parameter is passed as an associative array containing
pairs in the form  parameter => value . The NDBAL library defines the
following parameters (also known as  options  in this documentation):
 - prefix     -- prefix which will be used with  *queryf()  and
                 mapTable()  methods (optional),
 - driver     -- driver name
 - map        -- table map which will be used with  *queryf()  and
                 mapTable()  methods (optional),
 - map.*      -- table map entry.
 - persistent -- if true driver will use persitent connection if
                 implemented. True means a bool  true , a string value
                 not equal to any of: '0', 'false', 'off', 'no', 'f',
                 'n', 'null', '' or any other object == true .
Other parameters may (and probably will) be defined by a driver (for
example 'host', 'user' or 'password').

The usage of prefix and table map is described in the
NDBAL::mapTable()  description. The table map can be specified in two
ways: 1. as an array specified as a  map  parameter or, 2. as set of
map.*  parameters. For example, the fallowing configurations are equal
in meaning:

	// Configuration #1
	$conf = array('map' => array('asd' => 'qwe', 'qwe' => 'asd'))

	// Configuration #2
	$conf = array('map.asd' => 'qwe', 'map.qwe' => 'asd');

The method will return a new instance of a driver object on success,
or an error message otherwise. To check if there was an error one must
use the  is_string()  function. For example:

	$conf = array('driver'   => 'pgsql',
	              'host'     => 'example.com',
	              'username' => 'foo',
	              'password' => '*****');
	$DB = &NDBAL::createInstance($conf);
	if (is_string($DB)) {
	    echo("Could not create driver's instance: $DB.");
	    unset($DB);
	}

If  $prefix  is set, only those keys begining with that prefix will be
used in the configuration (The configuration itself will ignore the
prefixes). For example:

	$conf = array('foo.driver' => 'mysql',
	              'foo.host' => 'mysql.example.com');
	$DB = &NDBAL::createInstance($conf, 'foo.');

Is treated as:

	$conf = array('driver' => 'mysql',
	              'host' => 'mysql.example.com');
	$DB = &NDBAL::createInstance($conf);

And here are some examples on how  $url  and  $overwrite_url  work:

	// Each of these means the same thing:
	NDBAL::openURL('foo://example.com');
	NDBAL::openURL('foo:', array('host' => 'example.com'));
	NDBAL::openURL('foo:', array('bar.host' => 'example.com'), 'bar.');
	NDBAL::openURL('foo://example.com',
	               array('host' => 'another.example'));
	NDBAL::openURL('bar://example.com',
	               array('driver' => 'foo'), '', true);

This method is available since NDBAL 1.1.0.2 (in NDBAL 1.1.0.1 it was
available under different name; see  NDBAL::openURL() ).


======= 4.2.4. defaultErrorHandler() method

	void defaultErrorHandler(&$caller, string $type,
	                         string $message, mixed $argument)

This is the default error handler. It should not be used directly but
only as an error handler (set using  NDBAL::setErrorHandler()  or
NDBAL::setGlobalErrorHandler() ). This method formats a log entry
which is echoed by default; however it can be changed using the
NDBAL::setDefaultErrorHandlerOutput()  method.


======= 4.2.5. endTransaction() method

	bool endTransaction([ $commit = null ])

Ends a transaction and disables auto ignore mode (if started). Returns
true  on success, false  on failure. Auto ignore mode is disabled only
if the transaction was stopped successfully.

If  $commit  is  true  or  null  and the last query was successful
then the changes made during the transaction will be committed;
otherwise they will be canceled.


======= 4.2.6. getCapabilities() method

	int getCapabilities()

Returns the capabilities flag, which indicates the capabilities of the
current driver. You can then use binary operators (binary OR ("|") and
binary AND ("&")) and  NDBAL_C_*  constants (see above) to check what
the driver is capable of. See the description of  NDBAL::isCapableOf()
for more details.


======= 4.2.7. getDBInfo method

	public array getDBInfo()
	public string getDBInfo(string $key [, string $default = null])

Returns informations about the database to which you are connected. It
is very likely that if you are disconnected some data will be
unavailable or may be incorrect.

If the method is called without arguments it will return an array of
pairs in the format  key => value . If it is called with arguments it
will return a string with a value of specified  $key  (the 1st
argument), or a value of  $default  (the 2nd argument) if the
specified key was not found.

The following is list of required, recommended and suggested names for
driver or database info:
 * codename  -- server's codename (eg. 'pgsql' or 'mysql')
 * name      -- server's full name (eg. 'PostgreSQL' or 'MySQL')
 - version   -- server's version (eg. 7.4.2 or 3.51.06)
 - www       -- URL of server's homepage (eg. 'www.postgresql.org')
 - host      -- server's hostname (eg. 'db.example.com')
 - port      -- server's port number (eg. 5432 or 2206)
 - user      -- username (eg. 'zenon')
Keys marked with a star are always available (or at least should be
according to the documentation. Wait.. this is the documentation ;) )

	// An example:
	printf('Connected to %s server version %s on %s:%s using ' .
	       '%s version %s by <a href="mailto:%s">%s</a> and ' .
	       '%s library version %s by %s', $DB->getDBInfo('name'),
	       $DB->getDBInfo('version', 'N/A'),
	       $DB->getDBInfo('host', 'N/A'), $DB->getDBInfo('port', 'N/A'),
	       $DB->getDriverInfo('name'),
	       $DB->getDriverInfo('version', 'N/A'),
	       $DB->getDriverInfo('email', 'N/A'),
	       $DB->getDriverInfo('author', 'N/A'),
	       NDBAL::getLibInfo('name'),
	       NDBAL::getLibInfo('version', 'N/A'),
	       NDBAL::getLibInfo('author'));


======= 4.2.8. getDriverInfo() method

	public static array getDriverInfo()
	public static string getDriverInfo(string key
	                                   [, string default = null] )

Returns information about the driver.

If it is called without arguments it will return an array of pairs in
the form  key => value . If it is called with arguments it will return
a string with a value of specified  $key  (the 1st argument), or a
value of  $default  (the 2nd argument) if the specified key was not
found.

The following is list of required, recommended and suggested names for
driver's or database's info:
 * codename  -- driver's codename (eg. 'pgsql' or 'mysql')
 * name      -- driver's name (eg. 'NDBAL PostgreSQL Driver')
 + version   -- driver's version (eg. 0.1.0 or CVS-041103)
 + revision  -- driver's revision (eg. 1.1)
 + date      -- driver's date (eg. '2004/04/01 23:45:12')
 - www       -- driver's homepage (eg. 'ndbal.projektcode.org')
 - author    -- driver's author (eg. 'Zenon from Abwera')
 - email     -- author's email address (eg. 'zenon@abwera.com')
Keys marked with a star are always available (if they are not then it
is a bug). The plus sing is just a note for driver's developers that
they should try to implement keys marked with + before any other
(those keys are recommended).

For an example see the description of  NDBAL::getDBInfo()


======= 4.2.9. getDatabaseInfo() method

	public static array getDatabaseInfo()
	public static string getDatabaseInfo(string key
	                                     [, string default = null] )

Returns information about the database.

If it is called without arguments it will return an array of pairs in
the form  key => value . If it is called with arguments it will return
a string with a value of specified  $key  (the 1st argument), or a
value of  $default  (the 2nd argument) if the specified key was not
found.

The following is list of required, recommended and suggested names for
driver's or database's info:
 * codename  -- server's codename (eg. 'pgsql' or 'mysql')
 * name      -- server's full name (eg. 'PostgreSQL' or 'MySQL')
 - version   -- server's version (eg. 7.4.2 or 3.51.06)
 - www       -- URL of server's homepage (eg. 'www.postgresql.org')
 - host      -- server's hostname (eg. 'db.example.com')
 - port      -- server's port number (eg. 5432 or 2206)
 - user      -- username (eg. 'zenon')
Keys marked with a star are always available (if they are not then it
is a bug).

For example see description of  NDBAL::getDBInfo()


======= 4.2.10. getError() method

	string getError()

Returns an error message, or  null  if there was no error. This method
may return  false  or an empty string if no error occurred. Note also
that this function is not yet fully debugged so it may return
incorrect results (for example an error message although the last
operation succeeded).


======= 4.2.11. getErrorHandler() method

	callback getErrorHandler()

Returns a callback to a current error handler set for the object.
For more information about error handling see
NDBAL::setGlobalErrorHandler() , NDBAL::setErrorHandler()  and
NDBAL::defaultErrorHandler() .


======= 4.2.12. getGlobalErrorHandler() method

	static callback globalErrorHandler()

Returns a callback to a current global error handler. See
NDBAL::setGlobalErrorHandle(), NDBAL::setErrorHandler()  and
NDBAL::defaultErrorHandler()  for more info.


======= 4.2.13. getGlobalStats() method

	static array getGlobalStats()
	static numeric getGlobalStats(string $name)

Returns statistics on queries executed using any driver created with
the NDBAL library. It works similarly to  NDBAL::getStats()  so see
the description of that method for more details.


======= 4.2.14. getLastID() method

	int getLastID()
	int getLastID(string $table, string $column)

Returns the last id inserted in the most recent query to an auto
increment column. Note however that the driver must be capable (see
NDBAL::isCapableOf()  ) of  NDBAL_C_LAST_ID, or (since NDBAL 1.1.0.1)
the  NDBAL_C_LAST_ID_BY_NAME . If the driver lacks these capabilities
then the method will return  null . If there was an error, or the last
query was not an INSERT query then  false  will be returned. You
should also check the  NDBAL::getNextID() and  NDBAL::getNextIDs()
methods which can be used with drivers which don't support
NDBAL_C_LAST_ID  nor  NDBAL_C_LAST_ID_BY_NAME. 

NDBAL 1.1.0.1 introduced an alternative way of geting the last ID,
which requires the programmer to give the table and the auto increment
column name. This alternative way only needs to be used if a driver is
capable of  NDBAL_C_LAST_ID_BY_NAME  but not capable of
NDBAL_C_LAST_ID . The table and column name arguments are ignored if a
driver is capable of  NDBAL_C_LAST_ID  , so it is recommended to use
the new way of getting the last ID.

Example on how you should use  NDBAL::getLastID()  and
NDBAL::getNextIDs()  if only one of the constants are available:

	// Check if we are able to get ID
	if (!$DB->isCapableOfOneOf(NDBAL_C_ANY_LAST_ID | NDBAL_C_NEXT_ID)) {
	    die('Could not insert data.');
	}

	// Get IDs
	if ($DB->isCapableOf(NDBAL_C_NEXT_ID)) {
	    $ids = $DB->getNextIDs('topics.topics_id as tid, ' .
	                           'posts.post_id as pid');
	    if (!$ids && !$DB->isCapableOfOneOf(NDBAL_C_ANY_LAST_ID)) {
	        die('Error.');
	    }
	} else {
	    $ids = false;
	}

	// Insert topic
	$DB->queryf("INSERT INTO %t (topic_id, topic_title),
	             VALUES (" . ($ids?$ids['tic']:'null') . ",
	                     '%s')", 'topics', $title);
	$tic = $ids?$ids['tic']:$DB->getLastID('topics', 'topic_id');

	// Insert post
	$DB->queryf("INSERT INTO %t (post_id, topic_id, post_title,
	                             user_id),
	             VALUES (" . ($ids?$ids['pic']:'null') . ",
	                     %d, '%s', %d)", 'posts', $tic, $title,
	            $poster_id);
	$pic = $ids?$ids['pic']:$DB->getLastID('posts', 'post_id');

	// Insert post data
	$DB->queryf("INSERT INTO %t (post_id, post_data)
	             VALUES (%d, '%s')", 'posts_data', $pid, $message);


======= 4.2.15. getLastQuery() method

	string getLastQuery()

Returns the last query executed. Note that it does not have to be the
same query as specified in the last  query()  method. Some methods may
themselves execute queries (for example  NDBAL::startTransaction()  is
very likely to execute 'BEGIN' or something similar).


======= 4.2.16. getLastResult() method

	bool getLastResult()

Returns  true  if the last query was successful,  false  if it failed
or  null  if no query was executed.


======= 4.2.17. getNextID() method

	int getNextID(string $table, string $column)

Returns a unique id which can be used in an INSERT query. Each call of
this method will return a different id (most likely sequential, though
it depends mostly on the databese engine). This method is available
only if the driver is capable of  NDBAL_C_NEXT_ID .

This method can be used as an alternative to  NDBAL::getLastID()  (See
the description of this method for an example).

It takes as arguments a table and column name for which the ID should
be returned. It will return  false  on failure.

Since some drivers may have bugs and return ids which do not increment
automatically it is recommended that you use returned ids in an INSERT
query immediately after obtaining them. On the other hand, it is most
likely that this method will involve executing a query, so you should
try to avoid executing this method several times in a row and use
NDBAL::getNextIDs()  instead.


======= 4.2.18. getNextIDs() method

	array getNextIDs(string $list)
	array getNextIDs(array $list)

Returns unique ids which can be used in INSERT queries. Each call of
this method will return different ids (most likely sequential, though
it depends on the database engine). This method is available only if a
driver is capable of  NDBAL_C_NEXT_ID .

This method can be used as an alternative to  NDBAL::getLastID()  (See
the description of this method for an example).

It takes as an argument a string defined as follows:

	argument   = ( entry "," )* entry
	entry      = table "." column [ " as " name ]
	table      = id
	column     = id
	name       = id
	id         = ( letter | digit | "_" )+

For example:

	"users.user_id as uid, posts.post_id as pid, topics.topic_id"

The table and column sections are the table and column names for which
the method should return identifiers. The name section is an optional
name under which the identifier will be returned. If it is omitted, a
name of format:  <table>_<column>  is used (eg. in the above example
the topic id would be returned with name: "topics_topic_id").

Since NDBAL 1.1.0.1 this method can also take as an argument an
array. It must be an array of 2 or 3-element arrays. The 1st element
is the table name, the 2nd is the column name and the 3rd (optional)
is a key under which the next id should be returned. This method is a
bit faster in theory, however it is uglier :P since:

	"users.user_id as uid, posts.post_id as pid"

looks better then:

	array(array('users', 'user_id', 'uid'),
	      array('posts', 'post_id', 'pid'));

The method will return  null  if the driver is not capable of
NDBAL_C_NEXT_ID ,  false  if there was an error or associative array
with identifiers.

Since some drivers may have bugs and return ids which do not increment
automatically it is recommended that you use returned ids in an INSERT
query immediately after collecting them. On the other hand, it is most
likely that this method will involve executing a query so you should
try to avoid executing this method several times in a row and call it
only once instead.


======= 4.2.19. getStats() method

	array getStats()
	numeric getStats(string $name)

Returns statistics on queries executed using the particular driver.

If used without an argument the function will return an associative
array containing the following pairs of keys and values:
 - queries -- number of queries executed,
 - failed  -- number of queries which failed,
 - succeed -- number of queries which succeeded,
 - time    -- time spent on executing queries (float number in sec).

If an argument is specified and is one of the names listed above then
the method will return the corresponding information. If the argument
is not one of the names listed then  false  will be returned.

	// Example 1, show time spent on queries:
	echo('Time spent on queries: ' .
	     number_format($DB->getStats('time'), 6) . 'sec.');

	// Example 2, show number of queries executed:
	$info = $DB->getStats();
	if ($info['queries']==0) {
	    echo('No queries were executed.');
	} elseif ($info['queries']==1) {
	    echo('1 query was executed (and it ' .
	         ($info['failed']=='0'?'succeed':'failed') . ').');
	} else {
	    echo($info['queries'] . ' queries were executed (' .
	         ($info['failed']==0?'all succeeded':
	         "${info['failed']} failed") . ').');
	}

	// Example 3, now imagine that  $connections  is an array of used
	// connections. The following code will show statistics about each
	// connection (script assumes that at least 1 query was executed):
	$all = NDBAL::getGlobalStats('queries');
	$time = NDBAL::getGlobalStats('time');
	echo("  # | queries (% of all) | time    (% of all)n");
	for ($i = 0; $i<count($connections); ++$i) {
	    $num = $connections[$i]->getStats('queries');
	    $t = $connections[$i]->getStats('time');
	    printf("%3d | %5d   ( %3.1f%% ) | %2.5f ( %3.1f%% )n",
	           $i, $n, $n/$all*100, $t/$time*100);
	}


======= 4.2.20. getTableMap() method

	array getTableMap()

Returns the table map specified during the creation of an object. See
NDBAL::mapTable()  and  NDBAL::createInstance()  for more details.


======= 4.2.21. getTablePrefix() method

	string getTablePrefix()

Returns the table prefix specified during the creation of an
object. See  NDBAL::mapTable()  and  NDBAL::createInstance()  for more
details.


======= 4.2.22. getTransactionMode() method

	int getTransactionMode()

Returns the current transaction mode. The following modes are defined:
 - NDBAL_TM_NO            -- driver is not in transaction
 - NDBAL_TM_TRANSACTION   -- driver is in transaction
 - NDBAL_TM_AUTO_IGNORE   -- driver is in auto ignore mode
 - NDBAL_TM_AUTO_IGNORE_TRANSACTION -- driver is in transaction and
                                       auto ignore mode

Auto ignore mode means that driver does not execute any queries after
the first failed query and continues to ignore them until auto ignore
mode is disabled.


======= 4.2.23. isCapableOf() method

	bool isCapableOf(int $test)

Returns  true  if the driver is capable of all things specified by
$test . You should use the  NDBAL_C_*  constants as arguments.

A list of the constants and their meanings:
 - NDBAL_C_NUM_ROWS        -- returning the number of rows in result
 - NDBAL_C_AFFECTED_ROWS   -- returning the number of affected rows
 - NDBAL_C_TRANSACTION     -- transactions
 - NDBAL_C_PING            -- pinging the server (to check the
                              connection)
 - NDBAL_C_LAST_ID         -- returning the id of the auto increment
                              column in last INSERT query
 - NDBAL_C_NEXT_ID         -- returning a unique auto incrementing id
                              which can be used in an INSERT query
 - NDBAL_C_SEEK            -- moving the result pointer
 - NDBAL_C_LAST_ID_BY_NAME -- returning the id of the auto increment
                              column in last INSERT query (if table
                              name and auto increment column name is
                              given)
 - NDBAL_C_BUFFERED        -- NDBAL_C_NUM_ROWS and NDBAL_C_SEEK
 - NDBAL_C_ANY_LAST_ID     -- NDBAL_C_LAST_ID and NDBAL_C_LAST_ID_BY_NAME

Example:

	if ($DB->isCapableOf(NDBAL_C_NUM_ROWS))
	    echo('Driver is capable of returning number of rows.');
	if ($DB->isCapableOf(NDBAL_C_TRANSACTION))
	    echo('Driver is capable of transactions.');
	if ($DB->isCapableOf(NDBAL_C_NUM_ROWS | NDBAL_C_AFFECTED_ROWS))
	    echo('Driver is capable of returning number of ' .
	         'returned and number or changed rows in query.');
	if ($DB->getCapabilities() &
	    (NDBAL_C_NUM_ROWS | NDBAL_C_AFFECTED_ROWS)!==0)
	    echo('Driver is capable of returning number of ' .
	         'returned and number or changed rows in query.');


======= 4.2.24. isCapableOfOneOf() method

	bool isCapableOfOneOf(int $test)

Returns  true  if the driver is capable of one of the arguments
specified by  $test . You should use the  NDBAL_C_*  constants as
arguments (see also  NDBAL::isCapableOf() ).

Example:

	if ($DB->isCapableOfOneOf(NDBAL_C_ANY_LAST_ID))
	    echo('Last UID is: ' . $DB->getLastID('users', 'user_id'));

This method is available since NDBAL 1.1.0.1.


======= 4.2.25. isDriverLoaded() method

	static bool isDriverLoaded(string $name)

Returns  true  if the specified driver is loaded,  false  otherwise.


======= 4.2.26. loadDriver() method

	static string loadDriver(string $name)

Loads a driver, returns an empty string on success or an error
message on failure.


======= 4.2.27. mapTable() method

	string mapTable(string $name)

If the  $name  argument is in the table map which was specified during
object creation ( NDBAL::createInstance() ) then this method will
return the mapped name. Otherwise it will add a table prefix (see
NDBAL::getTablePrefix() ) to  $name  and return it. See the
description of  NDBAL::createInstance()  for more details.

This method is used by most of the methods which execute queries. It
allows you to transparently add a prefix to a table's name so that you
can use the same script (with a different prefixes) on the same
database for multiple sites. It also allows you to transparently
change the table's name. It can be used to share some tables between
two or more scripts. For example:

	// Configuration of script A
	$conf = array(
	    'driver' => 'driver',
	    'prefix' => 'foo_');

	// Configuration of script B
	$conf = array(
	    'driver' => 'driver',
	    'prefix' => 'bar_',
	    'map' => array('users' => 'foo_users'));

In this example, scripts A and B keep data in separate
tables (script A in foo_* and script B in bar_*) but they share the
users table.

Since NDBAL 1.1.0.1, this method checks to see if the table name is
valid; if not it will return  false . The error message generated may
be obtained using NDBAL::getError() .


======= 4.2.28. open() method

	bool open()

Connects to a database, opens files or does whatever is required to
allow the execution of queries. Returns  true  if operation is
successful and  false  otherwise.

If the connection is already open (see  NDBAL::opened() ) this
method will return true and do nothing.


======= 4.2.29. opened() method

	bool opened()

Returns  true  if a connection to the database is open (see
NDBAL::open()  method)  false  otherwise. Note that even if this
method returns  true  it doesn't have to mean that the connection is
active (check  NDBAL::ping()  if you want to test whether the
connection is active).


======= 4.2.30. parseURL() method

	static mixed parseURL(string $url, string $prefix = '')

Parses a URL; if successful returns an array which can be used as a
configuration array in the  createInstance()  method, otherwise
returns a string - error message. If the 2nd argument is set, all keys
in the resulting array will be prepended with the value of  $prefix .

The format of the URL might be described by this:

	url         = [ "!" ] driver [ ":" authority [ "/" dbname ]
	                             [ "?" query ] [ "#" opts ] ]
	authority   = [ [ user ] [ ":" pasword ] "@" ] [ host ] [ ":" port ]

	driver      = <digit or letter>+
	user        = <any char but one of "@:/?#">+
	password    = <any char but one of "@/?#">+
	host        = <any char but one of "/?#">+ |
	              "[" <any char but "]">+ "]"
	port        = <difit>+
	dbname      = <any char but one of "?#">+
	query       = <any char but "#">+
	options     = <any char>+

A full URL looks like this:

	!driver://user:password@host:port/dbname?other#options

Basically a standard URL with some small modifications. You can also
use the escape sequences used in all URLs: %## and + for space.

After the method determines the values for each part it parses the
parameters in the  query  section and uses these parameters to create
an initial array. Then the  drive ,  user , password ,  port ,  dbname
and  options  values are set. Finally the method checks whether the
host  section is a hostname, Internet address or socket path. It
strips '[' and ']' from the begining and end of the  host  section, if
alternative notation was used. It then checks if the 1st character of
the  host  section is ':'; if this is the case, and the 2nd character
is not ':' then the first character is removed and the  host  section
becomes the  socket  section. Adding a '!' before the URL will set a
persistent configuration option to  true . A few examples:

* null
  - driver:     null
* pgsql://john@example.com/johndb?prefix=foo_
  - driver:     pgsql
  - user:       john
  - host:       example.com
  - dbname:     johndb
  - prefix:     foo_
* !mysql://john:b%40r@[:/var/run/mysql/socket]/johndb
  - driver:     mysql
  - user:       john
  - passwrd:    b@r
  - socket:     /var/run/mysql/socket
  - dbname:     johndb
  = persistent: true

When the URL is parsed the method adds  $prefix  to all the keys in the
array and returns it.

This method is available since NDBAL 1.1.0.1.


======= 4.2.31. phpExt() method

	static string phpExt(string $new_ext = null)

This method returns the extension of PHP files used by the NDBAL
library. The extension includes the dot at the beginning: the default
value is '.php' . If an argument is specified it will be used to set a
new PHP extension and the old one will be returned. The PHP extension
is used by the  NDBAL::loadDriver()  method while loading a driver
from a file.


======= 4.2.32. ping() method

	bool ping()

Pings a database connection and tries to reconnect if it is
broken. Returns  true  if the connection is alive,  false
otherwise. If  the driver is not capable (see NDBAL::isCapableOf() )
of  NDBAL_C_PING  this method will return  null .

The behavior of transactions and other session-specific settings is
not yet defined, thus it is recommended to *not* use this method
unless you haven't set any special session variables and you're not in
transaction and/or auto ignore mode.


======= 4.2.33. query() method

	NDBALResult query(string $query [, bool $unbuffered = false] )

Executes the query and increments internal counters. If the query is
successful a new  NDBALResult  object will be return, otherwise it
will return false.

You should consider using the  NDBAL::queryf()  method instead as it is
much more secure and powerful then the  query()  method.

The optional 2nd argument instructs the driver as to whether it should
execute a buffered (when  false ; the default) or unbuffered (when
true ) query.

Unbuffered queries are useful when retrieving many large rows; however
it is not recommended if you are going to spend a lot of time
analyzing the result, as it could temporarily prevent other processes
from accessing the table (which is the case with MySQL). It is best to
use unbuffered queries only when printing a result to the output, with
no analysis. For example:

	echo('<table>');
	$result = &$DB->query("SELECT user_id, user_name,
	                              user_email, user_www
	                       FROM users ORDER BY user_name", false);
	while (($row = $result->fetch())) {
	    echo('<tr><td><a href="user.php?uid=' . $row['user_id'] .
	         '">' . $row['user_name'] . '</td>');
	    if ($row['user_email']) {
	        echo('<td><a href="mailto:' . $row['user_email'] .
	             '">' . $row['user_email'] . '</a></td>');
	    } else {
	        echo('<td>&nbsp;</td>');
	    }
	    if ($row['user_www']) {
	        echo('<td><a href="' . $row['user_www'] . '">' .
	             $row['user_www'] . '</a></td>');
	    } else {
	        echo('<td>&nbsp;</td>');
	    }
	    echo('</tr>');
	}
	echo('</table>');


======= 4.2.34. queryf() method

	NDBALResult queryf(string $format [, ...])

This method is more powerful and secure than the  NDBAL::query()
method because it decreases the probability of a SQL injection. With
this method you don't have to escape all variables or check if they
are valid - this will all be done automatically based on a query
format. See  NDBAL::squeryf()  for details.

This method formats a query and then passes it to the  NDBAL::query()
method, so essentially it operates the same way as  NDBAL::query()
and also returns the same values.

Example:

	$result = &$DB->queryf("SELECT * FROM %t
	    WHERE (user_id = %d) AND (session_id = '%s') AND
	          (session_ip = %id)", 'sessions', $_GET['uid'],
	                       $_GET['sid'], $_SERVER['REMOTE_ADDR']);


======= 4.2.35. setDefaultErrorHandlerOutput() method

	void setDefaultErrorHandlerOutput(callback $callback = null)

Sets a callback function used for printing/logging/etc. error
messages formatted by  NDBAL::defaultErrorHandler() . The callback
function must have the following definition:

	 void callback(string $message)

If the method is called with no arguments (or  null ) it will cause
the error messages to be echo()ed (the default behavior). If  false
is passed then error messages will be ignored.


======= 4.2.36. setErrorHandler() method

	void setErrorHandler(callback $new_handler = null)
	void setErrorHandler()

Replaces an error handler with a new one. To disable a local error
handler use  null  as an argument. If the method is called with no
arguments then the default error handler (see
NDBAL::defaultErrorHandler() ) will be set as a local error handler
for the object.

If a callback function is a static method you would usually use an
array (eg.  array('foo', 'errorHandler') ). With this function you can
use an alternative format which does not involve arrays; You just have
to use a string following the pattern:  'foo::errorHandler' .

If the local handler is disabled the global one will be used (see
NDBAL::setGlobalErrorHandler() ), unless it is also disabled.

For more information about error handling see
NDBAL::setGlobalErrorHandler()  and  NDBAL::defaultErrorHandler() .


======= 4.2.37. setGlobalErrorHandler() method

	static void setGlobalErrorHandler(callback $new_handler)
	static void setGlobalErrorHandler()

Replaces an error handler with a new one. To disable the global error
handler use  null  as an argument. Calling this method with no
arguments will set the default error handler (see
NDBAL::defaultErrorHandler() ).

If a callback function is a static method you would usually use an
array (eg.  array('foo', 'errorHandler') ). With this function you can
use an alternative format which does not involve arrays; You just have
to use a string following the pattern:  'foo::errorHandler' .

When an error occurs the NDBAL engine tries to call a local error
handler (set using  NDBAL::setErrorHandler() ). If this is not set the
engine will try a global one (set using
NDBAL::setGlobalErrorHandler() ). If the global handler is also
disabled then no error handler will be called.

A callback function should have the following format:

	void callback(&$caller, string $type, string $message,
	              mixed $argument)

$caller  is either a driver instance (instance of  NDBAL  class) or
a result set (instance of  NDBALResult  class) (only when Log Driver is
used). If you are using PHP 5 you can use an  instance of  operator to
check where the call was made from. To be compatible with PHP 4 you
can use the  is_a()  function, which is implemented in NDBAL for PHP
versions older than 4.3.0.

	// PHP 5+ code:
	function logger($caller, $type, $message, $argument) {
	    if ($caller instance of NDBALResult) {
	        // do something.. for example:
	        echo("From result: $messagen");
	    } else {
	        // do something else.. for example:
	        echo("From driver: $messagen");
	    }
	}

	// PHP 4+ code:
	function logger($caller, $type, $message, $argument) {
	    if (is_a($caller, 'NDBALResult')) {
	        // do something.. for example:
	        echo("From result: $messagen");
	    } else {
	        // do something else.. for example:
	        echo("From driver: $messagen");
	    }
	}

If you don't care whether the caller is a result or driver, but want
to get some info (like  log-id) from the driver you can use:

	function logger($caller, $type, $message, $argument) {
	    if (is_a($caller, 'NDBALResult'))
	        $caller = &$caller->getDB();
	    }
	    echo('Log #' . $caller->getInfo('log-id') .
	        ": $messagen");
	}

where  $type  is a type of error; a string matching the following
pattern:

	type       ::= [ extension ] ":" [ class "." ] id
	class      ::= [ class "." ] id
	extension  ::= id
	id         ::= [ char ] char
	valid-char ::= <any small letter, digit or "-">

The extension is so that driver developers can create specific error
types for the driver they are working on; however developers should
avoid creating new error types if well defined types matching the
errors already exist. Of course, you should avoid triggering errors
altogether :)

Error types are divided into classes with common specifications. For
example, the "query" class includes all errors which cause a query to
fail, the "msg" class includes debug messages, etc. In the
documentation classes will be referred to using a ".*" at the end; for
example: "query.*", "msg.*", "query.fmt.*".

The following error types and classes currently exist in NDBAL:
* ":connect.*"          -- errors involving connections - error while
                           opening, closing or maybe in the future
                           errors while transferring data
  * ":connect.close"      -- errors while closing a connection.
  * ":connect.open"       -- errors while opening a connection.
* ":fmt.*"              -- errors caused by invalid arguments passed
                           to the function which, however, do not
                           caused any query to fail.
  * ":fmt.config"         -- errors while creating a new instance of
                             a driver, caused by invalid configuration
                             options.
  * ":fmt.inv-tbl"        -- an argument is an invalid table name. The
                             argument is the name of the table
                             passed.
* ":msg.*"              -- not really errors but rather log
                           messages. This class is used by the
                           Log Driver. The argument in thisclass is
                           usually a name of a driver assigned by the
                           user. If you are writing an error handler
                           you can use it to identify which driver
                           triggered the log message. Note, however,
                           that the argument may also be  null  .
  * ":msg.close"          -- "closing" log message.
  * ":msg.open"           -- "opening" log message. Each opening log
                              message has a coresponding closing log
                              message.
* ":query.*"            -- errors while executing queries.
  * ":query.auto-ignore"  -- the driver is in auto ingore mode and the
                             last query failed.
  * ":query.empty"        -- the query succeeded but returned no
                             rows. This error is triggered by functions
                             which execute queries to get some
                             information required to execute another
                             query, or return requested info.
  * ":query.failed"       -- query failed. The argument is an executed
                             query.
  * ":query.fmt.*"        -- query was not executed because of invalid
                             arguments passed to the function.
    * ":query.fmt.empty"    -- an empty array/string was passed as an
                               argument.
    * ":query.fmt.inv"      -- there was some invalid argument.
    * ":query.fmt.inv-col"  -- argument is an invalid column name.
    * ":query.fmt.inv-tbl"  -- argument is an invalid table name.
    * ":query.fmt.nas"      -- an argument is not a string.
    * ":query.fmt.too-few"  -- too few arguments.
  * ":query.last-failed"  -- last query failed. The current query
                             required the last query to succeed.
* "multidb:*"           -- errors defined by MultiDB Driver
  * "multidb:no-db"       -- no database was selected.
  * "multidb:query.*"
    * "multidb:query.no-db" -- query could not be executed
                               because no database was selected.
  * "multidb:tbl.*"       -- errors while mapping a table. If an error
                             from this class occurs after calling a
                             method other than  mapTable()  , another
                             error will immediately be
                             triggered. e.g. ":query.fmt.inv-tbl". The
                             argument is a table name which caused the
                             error.
    * "multidb:tal.not-assigned" -- table is not assigned to any
                                    database.
    * "multidb:tbl.diff-db" -- table is assigned to  a different
                               database. This error occures if
                               databases are locked and the user tried
                               to select another database using the
                               mapTable()  method, or if tables
                               assigned to two different databases
                               were used in one query.

$message  is an error message and  $argument  is an argument which
differs for each type of error. The error message may be different
from the message returned by $caller->getError() ( $message  is
usually produced by an NDBAL engine, while the string returned by
$caller->getError()  might be an error message returned by a database
engine).

See  NDBAL::defaultErrorHandler()  and  NDBAL::setErrorHandler()  for
more info.


======= 4.2.38. squeryf() method

	string squeryf(string $format [, ...] )

This method formats a query according to a certain pattern.
Essentially, the  addslashes()  method is called before any string is
added to the query, greatly reducing the chance of SQL injections
occurring. It also provides some other features which may be handy
when working with SQL.

The format string is composed of zero or more directives: ordinary
characters (excluding %) that are copied directly into the result, and
conversion specifications, each of which fetches its own
parameter. Each conversion specification consists of a percent sign
(%), followed by one or more of the following elements (in order):

1. An optional padding character which specifies the character used
   for padding the results to obtain the correct string size. This may
   be a space character or a 0 (zero character); the default is to pad
   with spaces. An alternate padding character can be specified by
   prefixing it with a single quote (').

2. An optional alignment character which states if the result should
   be left-justified or right-justified. The default is
   right-justified; a - character will make it left-justified.

3. An optional integer which specifies the width of the result
   (ie. how many characters (minimum) the conversion should result
   in).

4. An optional precision integer which specifies the number of decimal
   digits displayed for floating-point numbers. This option has no
   effect for types other than float.

5. An optional ip2long conversion flag - 'i' character. If present the
   argument will first be converted into integer using the  ip2long()
   function.

6. A type character which specifies a type for the argument
   data. There are 3 groups of types:

   * literal percent character (no argument is required and all above
     elements (from 1 to 5) *MUST* be omitted):
     % - a literal percent character, no argument is required

   * strings (all above elements (from 1 to 5) *MUST* be omitted):
     s - the argument is treated as a string and the  addslashes()
         method is called before the argument is added to the result
     S - the argument is treated as a string and added to the result
         directly, NO SLASHING OR ESCAPING OF SPECIAL CHARS IS DONE.
         This format can lead to POTENTIAL SQL INJECTIONS, so use it
         carefully
     I - the argument is treated as an integer and is converted into a
         string using the  long2ip()  function, then added to the
         result 
     t - the argument is treated as a string and is first mapped using
         the  NDBAL::mapTable()  method; it is then processed in the
         same way as the  T  type
     T - the argument is treated as a string and added to the result,
         unless it contains any character which is not a letter, a
         digit or '_'; in that case the method returns false and
         triggers an error

   * numbers:
     b - the argument is treated as an integer and presented as a
         binary number
     c - the argument is treated as an integer and presented as the
         character with that ASCII value. Note that all characters are
         appended to the result literally and are *NOT* ESCAPED, so
         using this format may cause a potentially DANGEROUS character
         to be added (POSSIBLY CAUSING SQL INJECTION)
     d - the argument is treated as an integer and presented as a
         (signed) decimal number
     u - the argument is treated as an integer and presented as an
         unsigned decimal number
     f - the argument is treated as a float and presented as a
         floating-point number
     o - the argument is treated as an integer and presented as an
         octal number
     s - the argument is treated as and presented as a string
     x - the argument is treated as an integer and presented as a
         hexadecimal number (with lowercase letters)
     X - the argument is treated as an integer and presented as a
         hexadecimal number (with uppercase letters)

The method returns either a formatted query, or false if the format or
one of the arguments were invalid or missing.


======= 4.2.39. startTransaction() method

	bool startTransaction([ $auto_ignore = false ])

Starts a transaction and optionally also enables auto ignore mode (see
NDBAL::getTransactionMode()  for details). Returns  true  on success,
false  on failure or  null  if the database does not support
transactions.

If  $auto_ignore  is  true  then auto ignore mode will enabled. This
will occur even if the transaction was not started.


======= 4.2.40. uquery() method

	NDBALResult uquery(string $query)

This is an alias of the  NDBAL::query()  method, as called with the
2nd argument set to  true  (instructs driver to run an unbuffered
query).


======= 4.2.41. uqueryf() method

This method works just like the  NDBAL::queryf()  method, with the
exception that it instructs driver to start an unbuffered query (if
the driver does not support unbuffered queries it will silently ignore
your request and execute a buffered query instead). See
NDBAL::queryf()  for more details.


===== 4.3. NDBALResult Class

An instance of this class is created when a query is successfully
executed. It can be used to fetch the results and other info about
the query.


======= 4.3.1. countRows() method

	int countRows()

Returns the number of rows in a result. This method can be used only
after a SELECT query, and only if the driver is capable (see
NDBALResult::isCapableOf() ) of NDBAL_C_NUM_ROWS . If driver is not
capable of it then  null  will be returned.


======= 4.3.2. countAffectedRows() method

	int countAffectedRows()

Returns the number of affected rows. This method can be used only
after an UPDATE, INSERT or DELETE query, and only if the driver is
capable (see  NDBALResult::isCapableOf() ) of  NDBAL_C_AFFECTED_ROWS .
If the driver is not capable of it then  null  will be returned.


======= 4.3.3. fetch() method

	array fetch()
	array fetch(int $num)

An alias of  NDBALResult::fetchRow() .


======= 4.3.4. fetchObject method

	object fetchObject()
	object fetchObject(int $num)

Returns either an object with properties which correspond to the
fetched row, or  false  if there are no more rows or an error. The
argument works the same way as in  NDBALResult::fetchRow() .

At the moment this method is implemented such that the NDBAL engine
creates the object from an array fetched using
NDBALResult::fetchRow() . It is therefore slower than
NDBALResult::fetchRow()  so if you don't need an object returned then
it might be more efficient to use that method.


======= 4.3.5. fetchRow() method

	array fetchRow()
	array fetchRow(int $num)

Returns either an associative array which corresponds to the fetched
row, or  false  if there are no more rows. The resulting array could
possibly have numeric indexes so you cannot make any assumptions about
it (for example that it has only keys, or that it has both keys and
indices).

If the argument is used then the row with the number specified by that
argument will be returned (rows are numbered from 0), unless the
driver is not capable of  NDBAL_C_SEEK , in which case  null  will be
returned. Calling this method with an argument is the same as calling
NDBALResult::seek()  first and then this method without arguments; for
example:

	$row = $result->fetchRow(3);

is the same as:

	$row = $result->seek(3)
	if ($row) {
	    $row = $result->fetchRow()
	}


======= 4.3.6. fetchRest() method

	array fetchRest()

Fetches the rest of the rows in an array. Calling this method is
equivalent to using:

	$arr = array();
	while (($row = $result->fetch())) {
	    $arr[] = $row;
	}


======= 4.3.7. fetchAll() method

	array fetchAll()

This method returns all rows (including the one already fetched). If
there are rows already fetched this method will try to move the
internal pointer to the first row (using  NDBALResult::seek() ). If
this operation fails the method will return  false  or  null, depending on
the value returned by  NDBALResult::seek() .

The behavior of  NDBALResult::fetchRow()  when used after executing
this method is not defined. It is most likely that the internal result
pointer will point to the last row, so  NDBALResult::fetchRow()  will
return  false  when called if  NDBALResult::seek()  isn't used.


======= 4.3.8. free() method

	bool free()

Frees the memory used to store the result. After this method is
executed no rows can be fetched. This may be used to save memory,
especially when working with large results.


======= 4.3.9. freed() method

	bool freed()

Returns  true  if results were freed,  false  otherwise


======= 4.3.10. getCapabilities() method

	int getCapabilities()

Returns the capabilities flag. You can then use binary operators (binary
OR ("|") and binary AND ("&")) and  NDBAL_C_*  constants to check what
the driver is capable of. See the description of  NDBAL::isCapableOf()
for more details.

The flags are usually identical to the ones returned by
NDBAL::getCapabilities() ; however they may sometimes differ (for
example when executing an unbuffered query).


======= 4.3.11. getDB() method

	NDBAL getDB()

Returns the database object which was used to execute the query which
created the current result set. Note that in future versions of the
NDBAL library this method may return  null  in some cases.


======= 4.3.12. getQueryString() method

	string getQueryString()

Returns the query string used with this result. Note that in future
versions of the NDBAL library this method may return  null  in some
cases.


======= 4.3.13. isCapableOf() method

	bool isCapableOf(int $test)

Returns  true  if the driver is capable of all things specified by
$test . You should use the  NDBAL_C_*  constants in the argument.

The flags are usually identical to the ones returned by
NDBAL::getCapabilities() ; however they may sometimes differ (for
example when executing an unbuffered query).

See  NDBAL::isCapableOf()  for more details.


======= 4.3.14. isCapableOfOneOf() method

	bool isCapableOfOneOf(int $test)

Returns  true  if the driver is capable of one of the things specified
by  $test . You should use the  NDBAL_C_*  constants in the argument
(see  NDBAL::isCapableOf() ). See also  NDBALResult::isCapableOf()
and  NDBAL::isCapableOfOneOf()  for more details.

This method is available since NDBAL 1.1.0.1.


======= 4.3.15. seek() method

	bool seek([int $num = 0 [ , bool $from_current = false ]])

Moves the internal result pointer to the specified position, but only
if the driver is capable of  NDBAL_C_SEEK ; if it isn't then the
method will return  null . If successful  true  will be returned,
false  otherwise.

 * If  $from_current  is  false  (the default) and  $num  is greater
   than or equal to 0 then the method will try to move the pointer to
   the specified position.
 * If  $from_current  is  false  and  $num  is less than zero the
   method will try to move the pointer to the specified position,
   counting from the end.
 * If  $from_current  is  true  the method will try to move the
   pointer  $num  positions from the current position (forward if
   $num  is positive or backward if it is negative).

The method returns  true  on success,  false  otherwise.


===== 4.4. Other


======= 4.4.1. is_a() function

    bool is_a(object $object, string $class_name);

Since NDBAL 1.1.0.2 the library implements an  is_a()  function,
introduced in PHP 4.2.0. This function returns  true  if  $object  is
an instance of class  $class_name  (an instance of that class or of
any of it's subclasses).


=== 5. Drivers

NDBAL includes drivers for PostgreSQL, MySQL and SQLite databases. It
also provides Log and Multi Database pseudo drivers, and of course
anyone can write drivers for other databases.


===== 5.1. PostgreSQL Driver

This driver allows the user to use a PostgreSQL database.

PHP: 4.0.0 or greater
Extensions: pgsql
Persistent connections: implemented

Configuration options:
 - host     -- hostname where the server is located
 - port     -- port on which the server listens for connections
 - tty      -- tty name
 - options  -- additional options
 - dbname   -- database name
 - user     -- username
 - password -- user's password
 - socket   -- if tty is absent socket is checked to allow use of URLs

Using URL: The full URL looks something like:

	pgsql://user:password@host:port/dbname?prefix=foo_#options
	pgsql://user:password[:tty]/dbname?prefix=foo_#options

Available info:
 * Database:
    * codename  -- 'pgsql'
    * name      -- 'PostgreSQL'
    * www       -- 'www.postgresql.org'
    - host      -- server's hostname (eg. 'db.example.com')
    - port      -- server's port number (eg. 5432 or 2206)
    - user      -- username (eg. 'zenon')
    - tty       -- server's tty
    - options   -- connection options
 * Driver:
    * codename  -- 'pgsql'
    * name      -- 'NDBAL PostgreSQL Driver'
    - version   -- driver's version (eg. 0.1.0)
    * revision  -- driver's revision (eg. 1.1)
    * date      -- driver's date (eg. '2004/04/01 23:45')
    * author    -- 'Michal Nazarewicz'
Info marked with an * is always available; those marked with - can
sometimes be missing.

Capabilities: NDBAL_C_NUM_ROWS, NDBAL_C_AFFECTED_ROWS,
NDBAL_C_TRANSACTION (PostgreSQL 7.1+ required), NDBAL_C_PING (PHP 4.2+
required), NDBAL_C_NEXT_ID, NDBAL_C_LAST_ID_BY_NAME and NDBAL_C_SEEK.


===== 5.2. MySQL Driver

This driver allows the user to use a MySQL database.

PHP: 4.0.5 or greater
Extensions: mysql
Persistent connections: implemented

Configuration options:
 - host     -- hostname where the server is located
 - port     -- port on which the server listens for connections
 - socket   -- server's socket
 - user     -- username
 - password -- user's password
 - dbname   -- database name  [REQUIRED]
Notes: If host is specified - socket is ignored.
       If host is not specified - port is ignored

Using URL: The full URL looks something like:

	mysql://user:password@host:port/dbname?prefix=foo_
	mysql://user:password[:/socket]/dbname?prefix=foo_

Available info:
 * Database:
    * codename  -- 'mysql'
    * name      -- 'MySQL'
    * www       -- 'www.mysql.com'
    + version   -- server's version (eg. '4.0.1-alpha')
    + host_info -- describing the type of connection in use
    + protocol  -- the protocol version used by connection (eg. 10)
    * client    -- client's version (eg. '3.23.39')
    * dbname    -- database name (eg. 'test')
    - host      -- server's hostname (eg. 'db.example.com')
    - port      -- server's port number (eg. 5432 or 2206)
    - socket    -- server's socket (eg. '/home/zenon/mysql.666')
    - user      -- username (eg. 'zenon')
 * Driver:
    * codename  -- 'mysql'
    * name      -- 'NDBAL MySQL Driver'
    - version   -- driver's version (eg. 0.1.0)
    * revision  -- driver's revision (eg. 1.1)
    * date      -- driver's date (eg. '2004/04/01 23:45')
    * author    -- 'Michal Nazarewicz'
Info marked with a * is always available; those marked with - can
sometimes be missing. Those marked with + are available only while
connected.

Capabilities: NDBAL_C_NUM_ROWS, NDBAL_C_AFFECTED_ROWS,
NDBAL_TRANSACTION (MySQL 3.23.17+ required), NDBAL_C_PING (PHP
4.3.0+ required), NDBAL_C_LAST_ID and NDBAL_C_SEEK.


===== 5.3. SQLite Driver

This driver allows user to use SQLite library.

PHP: PHP Manual says 5+ but you can have the extension installed in
     earlier versions also.
Extensions: sqlite

Configuration options:
 - filename -- database filename  [REQUIRED]
 - socket   -- if filename is empty socket is used to allow usage of URLs

Using URL: The full URL looks something like:

	sqlite://[:/path/to/database.db]?prefix=foo_

Available info:
 * Database:
    * codename  -- 'sqlite'
    * name      -- 'SQLite'
    * version   -- SQLite library version
    * www       -- 'www.sqlite.org'
    * filename  -- database filename (eg. './data/database.sqlite')
 * Driver:
    * codename  -- 'sqlite'
    * name      -- 'NDBAL SQLite Driver'
    - version   -- driver's version (eg. 0.1.0)
    * revision  -- driver's revision (eg. 1.1)
    * date      -- driver's date (eg. '2004/04/01 23:45')
    * author    -- 'Michal Nazarewicz'
Info marked with a * is always available; those marked with - can
sometimes be missing.

Capabilities: NDBAL_C_NUM_ROWS, NDBAL_C_TRANSACTION,
NDBAL_C_LAST_ID and NDBAL_C_SEEK.

Note: While I was testing the library, the  sqlite_changes()  method
was returning  false  in all situations. Due to this bug I was forced
to disable support for  NDBAL_C_AFFECTED_ROWS . This issue will be
investigated in future versions.


===== 5.4. Null Driver

This driver does absolutely nothing except attempt to return valid
data. It can be used with the Log driver to test the behavior of PHP
scripts. It can be also used as an example on how to write a driver.

PHP: Any
Extensions: None required

Configuration options: None.

Using URL: The full URL looks something like:

	null:

Available info:
 * Database:
    * codename  -- 'null'
    * name      -- 'Null Database'
    * id        -- an unique id
 * Driver:
    * codename  -- 'null'
    * name      -- 'NDBAL Null Driver'
    - version   -- driver's version (eg. 0.1.0)
    * revision  -- driver's revision (eg. 1.1)
    * date      -- driver's date (eg. '2004/04/01 23:45')
    * author    -- 'Michal Nazarewicz'
Info marked with a * is always available; those marked with - can
sometimes be missing.

Capabilities: NDBAL_C_NUM_ROWS (always returns 0),
NDBAL_C_AFFECTED_ROWS (returns 0), NDBAL_C_TRANSACTION (returns true),
NDBAL_C_PING (returns true), NDBAL_C_LAST_ID (returns sequential
numbers starting from 0), NDBAL_C_NEXT_ID (same as LAST_ID),
NDBAL_C_SEEK (returns false since there are no rows in result).


===== 5.5. Log Driver

This is a pseudo driver which logs all action taking place. It can be
used for debugging, although the driver itself has not been debugged,
but it should work fine. This driver is available since
NDBAL 1.1.0.2.

PHP: Any
Extensions: None required

Configuration options:
 - logger     -- callback function used to log the driver's activity.
 - host       -- if logger is empty host is checked and used (if not
                 empty) to allow URL usage
 - argument   -- an argument which will be passed to the callback
                 function
 - dbname     -- if argument is empty dbname is checked and used (if
                 not empty) to allow URL usage
 - persistent -- if not empty and not  null  the value
                 will be used in the configuration of the real driver,
                 even if it is already available in the real driver's
                  configuration
 - There are also options used to specify a real driver, see section
   "Specifying a real driver" (this driver uses the 'options'
   alternative)
 - The standard options (see  NDBAL::createInstance() ), excluding
   driver  and  prefix  , are ignored.

Using URL: The full URL looks something like:

	log://callback_function/arg#foo://host/dbname?prefix=bar_

The part after '#' is a URL which points to the real driver to be used.

Available info:
 * Database:
    * logger-arg -- an argument set using the 'argument' configuration
                    option
    * log-id    -- a unique id
    Other info is taken from the real driver
 * Driver:
    * codename  -- 'log'
    * name      -- 'NDBAL Log Pseudo Driver'
    - version   -- driver's version (eg. 0.1.0)
    * revision  -- driver's revision (eg. 1.1)
    * date      -- driver's date (eg. '2004/04/01 23:45')
    * author    -- 'Michal Nazarewicz'
Info marked with a * is always available; those marked with - can
sometimes be missing.

Capabilities: The Capabilities set returned by the getCapabilities()
method is an intersection of the capabilities given by Log Driver (see
below) and those reported by the real driver.

The Log Driver is aware of the following capabilities: NDBAL_C_NUM_ROWS,
NDBAL_C_AFFECTED_ROWS, NDBAL_C_TRANSACTION. NDBAL_C_PING,
NDBAL_C_LAST_ID, NDBAL_C_NEXT_ID, NDBAL_C_SEEK and
NDBAL_C_LAST_ID_BY_NAME.

Since NDBAL 1.1.1, the Log Driver no longer uses its own callback
function; instead it uses the error handling mechanism available in the
NDBAL engine. The driver uses a 'msg.*' class of error tyoes. If the
'argument'  is set it will be passed as  $argument  to the error
handler. The default error handler prints it after the log driver's ID so
it can be used for identification.

If no callback function is specified, the default error handling
function ( NDBAL::defaultErrorHandler() ) will be used.


======= 5.5.1. The API

The Log driver provides one more method:


========= 5.5.1.1. getDatabase() method

	NDBAL &getDatabase()

Returns the database to which all requests are passed.


===== 5.6. MultiDB Driver

This is a HIGHLY EXPERIMENTAL pseudo driver which comes with
absolutely NO guarantee that it will work properly. If it is used, a
script could unknowingly execute a query using a single object which
would affect multiple different databases. The idea behind this driver
is to forward all queries to the proper driver, depending on the table
name used in the query.

PHP: Any
Extensions: None required

Configuration options:
 - persistent -- if not empty and not  null  the value will be used in
                 the configuration of each actual driver, even if it
                 is already available in the drivers' configuration
 - databases  -- array containing specified databases
 - db.*       -- same as above (and used if no 'databases' option) but
                 specified in a different format. With this format
                 you can pass an array without having already created
                 one.
 - For more information see the "Configuration" subsection below

Available info:
 * Database:
    * codename  -- 'multidb'
    * name      -- 'Multiple Databases'
    Other info is taken from the real driver
 * Driver:
    * codename  -- 'multidb'
    * name'     -- 'NDBAL Multi Databases Driver',
    - version   -- driver's version (eg. 0.1.0)
    * revision  -- driver's revision (eg. 1.1)
    * date      -- driver's date (eg. '2004/04/01 23:45')
    * author    -- 'Michal Nazarewicz'
Info marked with a * is always available; those marked with - can
sometimes be missing.

Capabilities: The capability set returned by the getCapabilities()
method is an intersection of the capablities understood by the MultiDB
Driver (see below) and of all those reported by the real drivers.

The driver is aware of the following capabilities: NDBAL_C_NUM_ROWS,
NDBAL_C_AFFECTED_ROWS, NDBAL_C_PING, NDBAL_C_LAST_ID, NDBAL_C_NEXT_ID,
NDBAL_C_SEEK and NDBAL_C_LAST_ID_BY_NAME.


======= 5.6.1. How does it work

In the configuration, the user specifies databases to be used with the
pseudo driver as well as assigns table names to those databases. For
example, a table name 'foo' might by assigned to one database and
'bar' to another. When using  queryf()  or  mapTable()  , the MultiDB
driver searches for a database to which the table name is assigned and
uses that database for the query/table mapping. If a table name is not
assigned to any database an error is triggered. Example:

	$cfg = array('databases' => array(
	    array('url' => '<something>', 'tables' => 'foo'),
	    array('url' => '<something>', 'tables' => 'bar')
	));
	$DB = NDBAL::createInstance('multidb', $cfg);

	// will be sent to 1st database
	$DB->queryf("SELECT * FROM %t", 'foo');
	// will be sent to 2nd database
	$DB->queryf("SELECT * FROM %t", 'bar');
	// will trigger error "Table baz is not assigned to any database"
	$DB->queryf("SELECT * FROM %t", 'baz');

Database changing can be disabled if necessary (see
MultiDB::lockDatabaseChanging() ); the function that checks whether
the table is assigned to the database can also be altered (see
MultiDB::setTableCheckCallback() ).

If a method is called and no table name is specified, the driver either
uses the database used in the last query, or one selected using the
MultiDB::selectDatabase()  or
MultiDB::selectDatabaseByTableName()  method. For example:

	$DB->selectDatabase(1);
	$map = $DB->getTableMap();


======= 5.6.2. Configuration

The most important part of the configuration is an array describing
all the databases. This array is the  'database'  option, which can be
replaced with several  'db.*'  options. For example:

	$cfg = array('databases' => array(
	  array('url' => 'null:', 'tables' => 'asd'),
	  array('url' => 'null:', 'tables' => 'qwe')
	);

	// is equivalent to
	$cfg = array(
	  'db.0.url' => 'null',
	  'db.0.tables' => 'asd',
	  'db.1' => array('url' => 'null', 'tables' => 'qwe')
	);

Each database is described by an array with following indices:
 - tables     -- an array or comma-separated list of tables asigned to
                 the database
 - preg       -- a regular Perl expression which matches all tables
                 assigned to the database. Used only if  'tables'  is
                 not set.
 - ereg       -- a regular POSIX expression which matches all tables
                 assigned to the database. Used only if  'tables'  and
                 'preg'  are not set.
 - eregi      -- a regular POSIX expression which matches all tables
                 assigned to the database, ignoring case. Used only if
                 'tables' , 'preg'  and  'ereg'  are not set.
 - fnmatch    -- a shell wildcard pattern which matches all tables
                 assigned to the database, ignoring case. Used only if
                 'tables' , 'preg' , 'ereg' and  'eregi'  are not
                 set. This option is available only if the  fnmatch()
                 function is available (PHP 4.3+).
 - obj        -- an instance of a driver. It might by useful if you
                 want to have more control over the process of creating
                 drivers.
 - url, conf, -- used if  'obj'  is not set to create a driver
   conf.*,      instance. Those options are used just like in the Log
                driver. See the "Specifying a real driver" section below.
One of  'tables' ,  'preg' , 'ereg' , 'eregi'  and  'fnmatch'  is
required. Also at least one of  'obj' ,  'conf'  or  'conf.*'  is
required.


======= 5.6.3. The API

The MultiDB driver provides few more methods.


========= 5.6.3.1. countDatabases() method

	int countDatabases()

Returns the number of databases defined in the current MultiDB driver.


========= 5.6.3.2. getCurrentDatabase() method

	NDBAL &getCurrentDatabase()

Returns the currently selected database (see
MultiDB::selectDatabase() ).


========= 5.6.3.3. getDatabase() method

	NDBAL &getDatabase(int $index)

Returns the database by the specified index; database indices start
from 0. Returns  null  if a database with the specified index does not
exist.


========= 5.6.3.4. getDatabaseByTable() method

	NDBAL &getDatabaseByTable(string $name)

Returns the database containing the specified table name. If the
table does not exist, returns  null .


========= 5.6.3.5. getTableCheckCallback() method

	callback getTableCheckCallback()

Returns a callback to a function used for checking whether the
specified table is assigned to the current database. The callback
function should be defined as follows:

	string tableCheck(NDBAL &$DB, string $name)

and should return an empty string if the table can be used with the
database or an error message otherwise. See
MultiDB::lockDatabaseChanging()  for more details.


========= 5.6.3.6. selectDatabase() method

	bool selectDatabase(int $index)

Selects the database by the specified index; database indices start
from 0. Returns  false  if a database with the specified index does
not exist, true  otherwise.

When a database is selected, all calls which do not contain a table
name will be passed to the selected database. Usually, methods which
take a table name as an argument will change the current database to
the one in which the table is assigned, but that behavior can be
configured using  MultiDB::lockDatabaseChanging()  and
MultiDB::setTableCheckCallback() .


========= 5.6.3.7. selectDatabaseByTable() method

	bool selectDatabaseByTable(string $name)

Selects (see MultiDB::selectDatabase() ) the database
assigned to the specified table name. If the table does not
exist, returns false ; true  otherwise.


========= 5.6.3.8. setTableCheckCallback() method

	void setTableCheckCallback()
	void setTableCheckCallback(callback $function = null)

Sets the table check callback (see MultiDB::getTableCheckCallback() )
to the one specified, or the default if the argument is omitted. If the
argument is  null then table checking is disabled. The default callback
function checks whether the table is assigned to the database
specified in the configuration.


========= 5.6.3.9. lockDatabaseChanging() method

	void lockDatabaseChanging(bool $lock = true)

Disables (if  $lock  is  true ) or enables (if  $lock  is  false )
database changing when a table name is assigned to a different
database. For example:

	$cfg = array('databases' => array(
	    array('url' => '<something>', 'tables' => 'foo'),
	    array('url' => '<something>', 'tables' => 'bar')
	));
	$DB = NDBAL::createInstance('multidb', $cfg);

	// will be sent to the 1st database
	$DB->queryf("SELECT * FROM %t", 'foo');
	// will be sent to the 2nd database
	$DB->queryf("SELECT * FROM %t", 'bar');
	// will trigger error "Table baz is not assigned to any database"
	$DB->queryf("SELECT * FROM %t", 'baz');

	$DB->lockDatabaseChanging();
	$DB->selectDatabaseByTable('foo');
	// will be sent to the 1st database
	$DB->queryf("SELECT * FROM %t", 'foo');
	// will trigger error "Table bar is assigned to different database"
	$DB->queryf("SELECT * FROM %t", 'bar');
	// will trigger error "Table baz is not assigned to any database"
	$DB->queryf("SELECT * FROM %t", 'baz');

	$DB->setTableCheckCallback(null);
	// all the following will be sent to the 1st database
	$DB->queryf("SELECT * FROM %t", 'foo');
	$DB->queryf("SELECT * FROM %t", 'bar');
	$DB->queryf("SELECT * FROM %t", 'baz');

	function mycheck(&$DB, $name) {
	    return $name==='baz'?'Name is baz!':'';
	}
	$DB->setTableCheckCallback('mycheck');
	// the following 2 will be sent to the 1st database
	$DB->queryf("SELECT * FROM %t", 'foo');
	$DB->queryf("SELECT * FROM %t", 'bar');
	// will trigger error "Name is baz"
	$DB->queryf("SELECT * FROM %t", 'baz');


===== 5.7. Specifying a real driver

Some drivers are not real drivers, rather they just pass all requests
to another "real" driver; for example the Log driver does
this. Because of this behavior a method to specify the real driver is
necessary. This section will describe such a method:

1. A driver may use some type of prefix (if so it will be
   documented in the section for that driver). If such a prefix is
   defined then only configuration options that begin with the prefix
   are used when the prefix is removed. Essentially a new
   configuration array is created from the old one. For example if we
   have an array:

	$conf = array(
	    'foo' => 'asd',
	    'bar' => 'qwe',
	    'baz.foo' => 'fgh',
	    'baz.bar' => 'rty'
	);

   and a prefix 'baz.', the array is treated as:

	$conf = array(
	    'foo' => 'fgh',
	    'bar' => 'rty'
	);

   with no prefix.

2. If the  dbobject  configuration option exists it becomes the driver
   object; no further steps are taken.

3. If the  url   configuration option exists it is treated as a URL,
   which is used to create an instance of a driver (see
   NDBAL::openURL() ).

4. (This step is optional; if used by a driver it will be documented.)
   If the  url  option does not exist then the  options  option is
   checked and used as a URL. With this step you can include any
   characters after the # character in the URL
   (eg. "log://logger#sqlite://[:./database]" ).

5. If the  conf  option exists it is used as a configuration array for
   the real driver. If it does not exist then options whose name
   begins with  conf  are used to create a configuration array.

After the steps are complete there must be a URL and/or configuration
array specified which are then used to create a driver instance. If
both the URL and configuration array are specified and they define the
same values then a value defined in the URL is taken (see also
NDBAL::openURL() ). An example using Log and PostgreSQL drivers:

	$conf = array(
	    'conf.prefix'  => 'foo_',
	    'conf.map.bar' => 'baz',
	    'conf.map.qwe' => 'asd'
	);
	$db = &NDBAL::openURL('log:#pgsql://pgsql.example.com/mydb', $conf);

NDBAL::openURL()  parses the URL and merges it with the  $conf  array,
making the following array:

	$conf = array(
	    'driver'       => 'log',
	    'options'      => 'pgsql://pgsql.example.com/mydb',
	    'conf.prefix'  => 'foo_',
	    'conf.map.bar' => 'baz',
	    'conf.map.qwe' => 'asd'
	);

This array is used in the algorithm described above. The Log driver
uses no prefix so all the entries are taken. Steps 2 and 3 fail, since
there is no 'dbobject' or 'url' option. Because the Log Driver uses
the 'options' alternative step 4 succeeds, and
'pgsql://pgsql.example.com/mydb'  is treated as a URL. Since there is
no 'conf' option the second part of step 5 applies, and this resulting
array is used as a configuration array:

	array(
	    'prefix'  => 'foo_',
	    'map.bar' => 'baz',
	    'map.qwe' => 'asd'
	);

A real driver is created using this array and URL.


=== 6. Writing a driver

If you want to write a driver you have to create two classes; the main
class and the result class. The process of writing a driver is not
very complicated,  it mostly involves overriding few abstract
methods.

The file where the classes are defined must be named
ndbal-<name>.drv.php  , where <name> is the driver's codename in
lowercase.

If you want to write a driver it is a good idea to copy the source
code of an existing driver and make the necessary changes.


===== 6.1. PHP5 vs PHP4 code

While writing code I wanted to be able to use PHP5 access control and
other useful features; however the code had to be PHP4 compatible. To
overcome this problem I have invented special PHP5 comments. The
comments hide incompatible code in the PHP4 version while allowing the
code to be included in the PHP5 version of the script.

The comments have to start with a string matching regexp:
/[*/]PHP5*[*/]  . When generating the PHP5 version of the script the
2nd character will be changed. If it is '*' it will be replaced with
'/' and if it is '/' it will be replaced with '*'. This can be done
with a simple SED script:

	s://(PHP5*[*/]):/*1:g
	t
	s:/*(PHP5*[*/])://1:g

In a result, you can use those comments in the following way:

       //PHP5*/ PHP5 code
       common code

       /*PHP5*/ PHP4 code
       common code

       //PHP5**
       PHP4 code
       // */ PHP5 code
       common code

       //PHP5*/ PHP5 code /*
       PHP4 code // */

So you can easily define access control for PHP5:

	//PHP5*/ public final
	function foo() {
		...
	}


===== 6.2. Notes about constructors

Since there is a difference when defining constructors in PHP4 and
PHP5, the following notes may be useful:

Define a method whose name is the same as a class name (so it will
be treated as a constructor in PHP4). This method must take all the
arguments which the constructor takes. The only thing the method
should do is to call a method named  __construct()  , with all
arguments.

Define a method called  __construct  (it will be treated as
a constructor in PHP5); this method will be the real constructor.
In case you want to call a parent's constructor you should use the
parent::__construct()  structure.

You can also use PHP5 comments described above to remove the first
method from PHP5 code.

	class Foo {
		//PHP5**
		function Foo($a) {
			this->__construct($a);
		} // */

		//PHP5*/ public
		function __construct($a) {
			$this->_a = $a;
		}

		//PHP5*/ protected
		var $_a = null;
	}

	class Bar extends Foo {
		//PHP5**
		function Bar($b, $a = 'qwe') {
			$this->__construct($b, $a);
		} // */

		//PHP5*/ public
		function __construct($b, $a = 'qwe') {
			parent::__construct($a);
			$this->_b = $b;
		}

		//PHP5*/ protected
		var $_b = null;
	}


===== 6.3. The main class

The main class of a driver must be named: NDBAL<Name>Driver where
<Name> is the driver's codename (case does not matter). This class
must extend the  NDBAL  class (or any other class which extends it).


======= 6.3.1. The constructor

	__construct(array $conf)

The constructor must take 1 argument - a configuration array. It is a
copy of the array which is passed to the  NDBAL::createInstance()
method; index 'driver' is always set to the driver's codename (in
lowercase).

The constructor should save any configuration options needed while
opening the connection. It must then run the parent constructor, which
is defined as follows:

	__construct(array &$conf, array $info, int $capabilities)

$conf  must be the same array which was passed to the constructor,
$info  must be an array of information about the database (not the
driver) which will then be available through the  NDBAL::getDBInfo()
method.  $capabilities  must be a set of flags specifying what the
driver is capable of (you should use the  NDBAL_C_*  constants and the
binary OR operator to set this argument).

Some information may be unknown until the connection is opened; for
example, it is unknown if the MySQL server supports transactions
without knowing the server's version. The database version is also
unknown until a connection is established. Because of this the  $info
array may lack some indices, and  $capabilities  may be incorrectly
set. To make common driver behavior you should specify in the  $info
array only information which is definitely available before a
connection, and then update the array in the  _open()  method. You
should also unset the information in the  _close()  method. For
$capabilities  you should set all the flags which driver is capable of
and then, if it turns out that the database cannot do something, you
should unset the required flags in the  _open()  method.


======= 6.3.2. _close() method  [required]

	bool _close()

This method must close the connection to the database, returning  true
if the connection is no longer open and  false  if it is still open (if
some kind of strange error occurred).


======= 6.3.3. _endTransaction() method

	_endTransaction(bool $commit)

This method is required only if driver is capable of
NDBAL_C_TRANSACTION ; if it is not then either don't override it, or
have it  return  null .

The method should end the transaction and commit the changes if
$commit  is  true , or cancel them (rollback) if  $commit  is  false .

It must return  true  if the transaction has completed,  false
otherwise. The occurrence of errors or warnings is less important than
whether the transaction has been completed or not.


======= 6.3.4. _getError() method  [required]

	string _getError()

This method must return the last error message or  null  if there was
no error. All names are checked by the NDBAL class so this method
doesn't have to check them.


======= 6.3.5. _getLastID() method

	int _getLastID()
	int _getLastID(string $table = null, string $column = null)

This method is required only if the driver is capable of
NDBAL_C_LAST_ID or  NDBAL_C_LAST_ID_BY_NAME ; if not the method either
should not be overridden or should return  null . If used, the method
must return the ID generated in the previous INSERT operation, or
false  on error.

In NDBAL 1.1.0.1, the  NDBAL_C_LAST_ID_BY_NAME  constant and two
additional arguments in this method were introduced. This was because
some databases don't have a  get_last_id()  function (or something
similar) but may provide functions which allow retrieving the last
inserted ID, provided the table name and the auto increment column
name is known (eg. PostgreSQL). If you are writing a driver for such a
database you should check those arguments; if they are  null  then
return  null , otherwise return the last inserted ID. If you are
writing a driver for a database which has something like
get_last_id()  then ignore them and the  NDBAL_C_LAST_ID_BY_NAME
constant.

All names are checked by the NDBAL class so this method doesn't have
to check them.


======= 6.3.6. _getNextID() method

	int _getNextID(string $table, string $column)

This method may be omitted in a driver, even if the NDBAL_C_NEXT_ID
constant is set, because the method is implemented in the NDBAL class
such that it calls the  _getNextIDs()  method.

This method must return a unique ID for column  $column  in table
$table (which can then be used in an INSERT query), or  false  on
error. Each time this method is called it must return different
(preferably sequential) IDs.

All names are checked by the NDBAL class so this method doesn't have
to check them.


======= 6.3.7. _getNextIDs() method

	array _getNextIDs(array $args)

This method is required only if a driver is capable of
NDBAL_C_NEXT_ID ; If not the method either shouldn't be overridden or
should return  null .

This method must return unique IDs for columns specified in the array
$args (which can be then used in INSERT query), or  false  on
error. Each time this method is called it must return different
(preferably sequential) IDs.

$args is a N-element array of 3-element arrays. The method returns
its results in a N-element array such that for each 3-element array an
index is created; this index is a unique id composed of a name (the
3rd element), a column (the 2nd element) and a table (the 1st
element). For example:

	// For the following $arg
	$ars = array(
		array('foo', 'bar', 'id1'),
		array('qwe', 'asd', 'id2')
	);
	// Method should return
	array(
		'id1' => <ID1>,
		'id2' => <ID2>
	);
	// Where  <ID1>  is a unique ID for column  bar  in table  foo
	// and    <ID2>  is a unique ID for column  asd  in table  qwe

All names are checked by the NDBAL class so this method doesn't have
to check them.


======= 6.3.8. _open() method  [required]

	bool _open()

This method should attempt to establish a connection with the
database, open files or do whatever is required to allow queries to
run. If successful, or the connection was already open, the method
must return  true ; otherwise false .


======= 6.3.9. _ping() method

	bool _ping()

This method is required only if the driver is capable of  NDBAL_C_PING
; otherwise it either shouldn't be overridden or should return  null .

The method should check whether the connection is alive, and if not
try to reconnect. It must return  true  if the connection is still
active, false  otherwise.


======= 6.3.10. _query() method  [required]

	NDBALResult _query(string $query, $unbuffered)

This method must execute  $query  and return a new instance of the
NDBALResult  class (which can be used to fetch the results of a query)
or  false  on error.

The returned object will probably be an instance of the class defined
in a driver; however developers may use the  NDBALEmptyResult
class. This class can be used for results with no rows (eg. an UPDATE
query). The constructor of this class is defined as follows:

	__construct(NDBAL &$db, string $query_string,
	              [ mixed $affected = 0 [, mixed $count = false ]])

The first argument is reference to the object used to execute a query,
the 2nd is a query string, the 3rd is a value which will be returned
by the  countAffectedRows()  method and the 4th is a value which will
be returned by the  countRows()  method.

For example, assume that we are writing the driver  NDBALFooDriver :
foo_query()  executes a query and returns  false  on failure or  true
on success, but the query does not return any rows;  foo_affected()
returns the number of affected rows. Our  _query()  method may look
something like:

	//PHP5*/ protected
	function _query($query, $unbuffered) {
	    $result = foo_query($query);
	    if ($result===false) {
	        return false;
	    } elseif ($result===true) {
	        return new NDBALEmptyResult($this, $query,
	                                    foo_affected());
	    } else {
	        return new NDBALFooResult($this, $query, $result);
	    }
	}

The  $unbuffered  argument states whether the driver should try to run
an unbuffered query; the result of such a query (most likely) doesn't
support seeking, etc. but is a bit faster. The driver may completely
ignore the value of this argument, since generally the result is the
same. Moreover, if the driver doesn't support buffered queries then
all queries will be unbuffered anyway.


======= 6.3.11. _startTranasction()

	bool _startTransaction()

This method is required only if the driver is capable of
NDBAL_C_TRANSACTION  ; otherwise it either shouldn't be overridden or
should return  null .

The method should start a transaction which will allow the
cancellation of all queries executed during the transaction.
It must return  true  if the transaction has been started,  false
otherwise. The occurrence of errors or warnings in this method is not
important.


===== 6.4. The result class

The result class must extend the  NDBALResult  class (or any other
class which extends it). It is used to fetch rows returned by a query
and retrieve other information.


======= 6.4.1. The constructor

The constructor must perform database specific tasks, then call the
parent's constructor (which is defined as follows):

	__construct(NDBAL &$db, string $query_string
	              [, bool $unbuffered = false])

All arguments should be supplied by the  _query()  method of the
main class. The last argument is only used to unset the  NDBAL_C_SEEK
and  NDBAL_C_NUM_ROWS  flags, in case the query was executed as an
unbuffered query.

There are no requirements for the definition of the constructor, but
it will likely look something like:

	__construct(NDBAL &$db, string $query_string,
	              resource $result [, bool $unbuffered = false])


======= 6.4.2. _countAffectedRows() method

	int countAffectedRows()

This method is required only if the driver supports
NDBAL_C_AFFECTED_ROWS . If so this method must return either the
number of rows affected by a query or  false  on error (or if the
query does not affect the database); otherwise the method must return
null.


======= 6.4.3. _countRows() method

	int countRows()

This method is required only if the driver supports
NDBAL_C_NUM_ROWS . If so the method must either return the number of
rows in the result or  false  on error (or if the query does not
return rows); otherwise the method must return  null.


======= 6.4.4. _fetch() method  [required]

	array _fetch()

This method must return either the next row from the result set (as an
associative array), or  false  if there are no more rows in the
result.


======= 6.4.5. _free() method

	bool _free()

This method should free the memory used by a result, returning  true
if successful (or if the result was already freed),  false
otherwise.


======= 6.4.6. _seek() method

	bool _seek(int $num)

This method is required only if the driver supports the
NDBAL_C_NUM_ROWS  constant. If so the method should move the internal
pointer to the row  $num  so that it will be returned by the next call
of the  _fetch()  method. It should return  true  if successful,
false  otherwise.


=== 7. Copyright


===== 7.1. Library

                   Nidhogg Database Abstract Layer
     Copyright 2004,2005 by Michal Nazarewicz <mina86/AT/tlen.pl>

This software is OSI Certified Open Source Software.
OSI Certified is a certification mark of the Open Source Initiative.

This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

A copy of the GNU Lesser General Public License is included in the
section entitled "GNU Lesser General Public License"


===== 7.2. Documentation

            Nidhogg Database Abstract Layer Documentation
     Copyright 2004,2005 by Michal Nazarewicz <mina86/AT/tlen.pl>

Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A
copy of the license is included in the section entitled "GNU Free
Documentation License".


===== 7.3. GNU Lesser General Public License

		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.

  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

			    NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

		     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!


===== 7.4. GNU Free Documentation License

		GNU Free Documentation License
		  Version 1.2, November 2002

 Copyright (C) 2000,2001,2002  Free Software Foundation, Inc.
     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

0. PREAMBLE

The purpose of this License is to make a manual, textbook, or other
functional and useful document "free" in the sense of freedom: to
assure everyone the effective freedom to copy and redistribute it,
with or without modifying it, either commercially or noncommercially.
Secondarily, this License preserves for the author and publisher a way
to get credit for their work, while not being considered responsible
for modifications made by others.

This License is a kind of "copyleft", which means that derivative
works of the document must themselves be free in the same sense.  It
complements the GNU General Public License, which is a copyleft
license designed for free software.

We have designed this License in order to use it for manuals for free
software, because free software needs free documentation: a free
program should come with manuals providing the same freedoms that the
software does.  But this License is not limited to software manuals;
it can be used for any textual work, regardless of subject matter or
whether it is published as a printed book.  We recommend this License
principally for works whose purpose is instruction or reference.

1. APPLICABILITY AND DEFINITIONS

This License applies to any manual or other work, in any medium, that
contains a notice placed by the copyright holder saying it can be
distributed under the terms of this License.  Such a notice grants a
world-wide, royalty-free license, unlimited in duration, to use that
work under the conditions stated herein.  The "Document", below,
refers to any such manual or work.  Any member of the public is a
licensee, and is addressed as "you".  You accept the license if you
copy, modify or distribute the work in a way requiring permission
under copyright law.

A "Modified Version" of the Document means any work containing the
Document or a portion of it, either copied verbatim, or with
modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of
the Document that deals exclusively with the relationship of the
publishers or authors of the Document to the Document's overall subject
(or to related matters) and contains nothing that could fall directly
within that overall subject.  (Thus, if the Document is in part a
textbook of mathematics, a Secondary Section may not explain any
mathematics.)  The relationship could be a matter of historical
connection with the subject or with related matters, or of legal,
commercial, philosophical, ethical or political position regarding
them.

The "Invariant Sections" are certain Secondary Sections whose titles
are designated, as being those of Invariant Sections, in the notice
that says that the Document is released under this License.  If a
section does not fit the above definition of Secondary then it is not
allowed to be designated as Invariant.  The Document may contain zero
Invariant Sections.  If the Document does not identify any Invariant
Sections then there are none.

The "Cover Texts" are certain short passages of text that are listed,
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
the Document is released under this License.  A Front-Cover Text may
be at most 5 words, and a Back-Cover Text may be at most 25 words.

A "Transparent" copy of the Document means a machine-readable copy,
represented in a format whose specification is available to the
general public, that is suitable for revising the document
straightforwardly with generic text editors or (for images composed of
pixels) generic paint programs or (for drawings) some widely available
drawing editor, and that is suitable for input to text formatters or
for automatic translation to a variety of formats suitable for input
to text formatters.  A copy made in an otherwise Transparent file
format whose markup, or absence of markup, has been arranged to thwart
or discourage subsequent modification by readers is not Transparent.
An image format is not Transparent if used for any substantial amount
of text.  A copy that is not "Transparent" is called "Opaque".

Examples of suitable formats for Transparent copies include plain
ASCII without markup, Texinfo input format, LaTeX input format, SGML
or XML using a publicly available DTD, and standard-conforming simple
HTML, PostScript or PDF designed for human modification.  Examples of
transparent image formats include PNG, XCF and JPG.  Opaque formats
include proprietary formats that can be read and edited only by
proprietary word processors, SGML or XML for which the DTD and/or
processing tools are not generally available, and the
machine-generated HTML, PostScript or PDF produced by some word
processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself,
plus such following pages as are needed to hold, legibly, the material
this License requires to appear in the title page.  For works in
formats which do not have any title page as such, "Title Page" means
the text near the most prominent appearance of the work's title,
preceding the beginning of the body of the text.

A section "Entitled XYZ" means a named subunit of the Document whose
title either is precisely XYZ or contains XYZ in parentheses following
text that translates XYZ in another language.  (Here XYZ stands for a
specific section name mentioned below, such as "Acknowledgements",
"Dedications", "Endorsements", or "History".)  To "Preserve the Title"
of such a section when you modify the Document means that it remains a
section "Entitled XYZ" according to this definition.

The Document may include Warranty Disclaimers next to the notice which
states that this License applies to the Document.  These Warranty
Disclaimers are considered to be included by reference in this
License, but only as regards disclaiming warranties: any other
implication that these Warranty Disclaimers may have is void and has
no effect on the meaning of this License.

2. VERBATIM COPYING

You may copy and distribute the Document in any medium, either
commercially or noncommercially, provided that this License, the
copyright notices, and the license notice saying this License applies
to the Document are reproduced in all copies, and that you add no other
conditions whatsoever to those of this License.  You may not use
technical measures to obstruct or control the reading or further
copying of the copies you make or distribute.  However, you may accept
compensation in exchange for copies.  If you distribute a large enough
number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and
you may publicly display copies.

3. COPYING IN QUANTITY

If you publish printed copies (or copies in media that commonly have
printed covers) of the Document, numbering more than 100, and the
Document's license notice requires Cover Texts, you must enclose the
copies in covers that carry, clearly and legibly, all these Cover
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
the back cover.  Both covers must also clearly and legibly identify
you as the publisher of these copies.  The front cover must present
the full title with all words of the title equally prominent and
visible.  You may add other material on the covers in addition.
Copying with changes limited to the covers, as long as they preserve
the title of the Document and satisfy these conditions, can be treated
as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit
legibly, you should put the first ones listed (as many as fit
reasonably) on the actual cover, and continue the rest onto adjacent
pages.

If you publish or distribute Opaque copies of the Document numbering
more than 100, you must either include a machine-readable Transparent
copy along with each Opaque copy, or state in or with each Opaque copy
a computer-network location from which the general network-using
public has access to download using public-standard network protocols
a complete Transparent copy of the Document, free of added material.
If you use the latter option, you must take reasonably prudent steps,
when you begin distribution of Opaque copies in quantity, to ensure
that this Transparent copy will remain thus accessible at the stated
location until at least one year after the last time you distribute an
Opaque copy (directly or through your agents or retailers) of that
edition to the public.

It is requested, but not required, that you contact the authors of the
Document well before redistributing any large number of copies, to give
them a chance to provide you with an updated version of the Document.

4. MODIFICATIONS

You may copy and distribute a Modified Version of the Document under
the conditions of sections 2 and 3 above, provided that you release
the Modified Version under precisely this License, with the Modified
Version filling the role of the Document, thus licensing distribution
and modification of the Modified Version to whoever possesses a copy
of it.  In addition, you must do these things in the Modified Version:

A. Use in the Title Page (and on the covers, if any) a title distinct
   from that of the Document, and from those of previous versions
   (which should, if there were any, be listed in the History section
   of the Document).  You may use the same title as a previous version
   if the original publisher of that version gives permission.
B. List on the Title Page, as authors, one or more persons or entities
   responsible for authorship of the modifications in the Modified
   Version, together with at least five of the principal authors of the
   Document (all of its principal authors, if it has fewer than five),
   unless they release you from this requirement.
C. State on the Title page the name of the publisher of the
   Modified Version, as the publisher.
D. Preserve all the copyright notices of the Document.
E. Add an appropriate copyright notice for your modifications
   adjacent to the other copyright notices.
F. Include, immediately after the copyright notices, a license notice
   giving the public permission to use the Modified Version under the
   terms of this License, in the form shown in the Addendum below.
G. Preserve in that license notice the full lists of Invariant Sections
   and required Cover Texts given in the Document's license notice.
H. Include an unaltered copy of this License.
I. Preserve the section Entitled "History", Preserve its Title, and add
   to it an item stating at least the title, year, new authors, and
   publisher of the Modified Version as given on the Title Page.  If
   there is no section Entitled "History" in the Document, create one
   stating the title, year, authors, and publisher of the Document as
   given on its Title Page, then add an item describing the Modified
   Version as stated in the previous sentence.
J. Preserve the network location, if any, given in the Document for
   public access to a Transparent copy of the Document, and likewise
   the network locations given in the Document for previous versions
   it was based on.  These may be placed in the "History" section.
   You may omit a network location for a work that was published at
   least four years before the Document itself, or if the original
   publisher of the version it refers to gives permission.
K. For any section Entitled "Acknowledgements" or "Dedications",
   Preserve the Title of the section, and preserve in the section all
   the substance and tone of each of the contributor acknowledgements
   and/or dedications given therein.
L. Preserve all the Invariant Sections of the Document,
   unaltered in their text and in their titles.  Section numbers
   or the equivalent are not considered part of the section titles.
M. Delete any section Entitled "Endorsements".  Such a section
   may not be included in the Modified Version.
N. Do not retitle any existing section to be Entitled "Endorsements"
   or to conflict in title with any Invariant Section.
O. Preserve any Warranty Disclaimers.

If the Modified Version includes new front-matter sections or
appendices that qualify as Secondary Sections and contain no material
copied from the Document, you may at your option designate some or all
of these sections as invariant.  To do this, add their titles to the
list of Invariant Sections in the Modified Version's license notice.
These titles must be distinct from any other section titles.

You may add a section Entitled "Endorsements", provided it contains
nothing but endorsements of your Modified Version by various
parties--for example, statements of peer review or that the text has
been approved by an organization as the authoritative definition of a
standard.

You may add a passage of up to five words as a Front-Cover Text, and a
passage of up to 25 words as a Back-Cover Text, to the end of the list
of Cover Texts in the Modified Version.  Only one passage of
Front-Cover Text and one of Back-Cover Text may be added by (or
through arrangements made by) any one entity.  If the Document already
includes a cover text for the same cover, previously added by you or
by arrangement made by the same entity you are acting on behalf of,
you may not add another; but you may replace the old one, on explicit
permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License
give permission to use their names for publicity for or to assert or
imply endorsement of any Modified Version.

5. COMBINING DOCUMENTS

You may combine the Document with other documents released under this
License, under the terms defined in section 4 above for modified
versions, provided that you include in the combination all of the
Invariant Sections of all of the original documents, unmodified, and
list them all as Invariant Sections of your combined work in its
license notice, and that you preserve all their Warranty Disclaimers.

The combined work need only contain one copy of this License, and
multiple identical Invariant Sections may be replaced with a single
copy.  If there are multiple Invariant Sections with the same name but
different contents, make the title of each such section unique by
adding at the end of it, in parentheses, the name of the original
author or publisher of that section if known, or else a unique number.
Make the same adjustment to the section titles in the list of
Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections Entitled "History"
in the various original documents, forming one section Entitled
"History"; likewise combine any sections Entitled "Acknowledgements",
and any sections Entitled "Dedications".  You must delete all sections
Entitled "Endorsements".

6. COLLECTIONS OF DOCUMENTS

You may make a collection consisting of the Document and other documents
released under this License, and replace the individual copies of this
License in the various documents with a single copy that is included in
the collection, provided that you follow the rules of this License for
verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute
it individually under this License, provided you insert a copy of this
License into the extracted document, and follow this License in all
other respects regarding verbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKS

A compilation of the Document or its derivatives with other separate
and independent documents or works, in or on a volume of a storage or
distribution medium, is called an "aggregate" if the copyright
resulting from the compilation is not used to limit the legal rights
of the compilation's users beyond what the individual works permit.
When the Document is included in an aggregate, this License does not
apply to the other works in the aggregate which are not themselves
derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these
copies of the Document, then if the Document is less than one half of
the entire aggregate, the Document's Cover Texts may be placed on
covers that bracket the Document within the aggregate, or the
electronic equivalent of covers if the Document is in electronic form.
Otherwise they must appear on printed covers that bracket the whole
aggregate.

8. TRANSLATION

Translation is considered a kind of modification, so you may
distribute translations of the Document under the terms of section 4.
Replacing Invariant Sections with translations requires special
permission from their copyright holders, but you may include
translations of some or all Invariant Sections in addition to the
original versions of these Invariant Sections.  You may include a
translation of this License, and all the license notices in the
Document, and any Warranty Disclaimers, provided that you also include
the original English version of this License and the original versions
of those notices and disclaimers.  In case of a disagreement between
the translation and the original version of this License or a notice
or disclaimer, the original version will prevail.

If a section in the Document is Entitled "Acknowledgements",
"Dedications", or "History", the requirement (section 4) to Preserve
its Title (section 1) will typically require changing the actual
title.

9. TERMINATION

You may not copy, modify, sublicense, or distribute the Document except
as expressly provided for under this License.  Any other attempt to
copy, modify, sublicense or distribute the Document is void, and will
automatically terminate your rights under this License.  However,
parties who have received copies, or rights, from you under this
License will not have their licenses terminated so long as such
parties remain in full compliance.

10. FUTURE REVISIONS OF THIS LICENSE

The Free Software Foundation may publish new, revised versions
of the GNU Free Documentation License from time to time.  Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.  See
http://www.gnu.org/copyleft/.

Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
License "or any later version" applies to it, you have the option of
following the terms and conditions either of that specified version or
of any later version that has been published (not as a draft) by the
Free Software Foundation.  If the Document does not specify a version
number of this License, you may choose any version ever published (not
as a draft) by the Free Software Foundation.

ADDENDUM: How to use this License for your documents

To use this License in a document you have written, include a copy of
the License in the document and put the following copyright and
license notices just after the title page:

    Copyright (c)  YEAR  YOUR NAME.
    Permission is granted to copy, distribute and/or modify this document
    under the terms of the GNU Free Documentation License, Version 1.2
    or any later version published by the Free Software Foundation;
    with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
    A copy of the license is included in the section entitled "GNU
    Free Documentation License".

If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
replace the "with...Texts." line with this:

    with the Invariant Sections being LIST THEIR TITLES, with the
    Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.

If you have Invariant Sections without Cover Texts, or some other
combination of the three, merge those two alternatives to suit the
situation.

If your document contains nontrivial examples of program code, we
recommend releasing these examples in parallel under your choice of
free software license, such as the GNU General Public License,
to permit their use in free software.