=== modified file 'includes/database/database.inc'
--- includes/database/database.inc	2008-08-21 19:36:35 +0000
+++ includes/database/database.inc	2008-08-30 13:57:00 +0000
@@ -54,59 +54,59 @@ define('DB_ERROR', 'a515ac9c2796ca0e23ad
  * database servers, so that is abstracted into db_query_range() arguments.
  * Finally, note the PDO-based ability to foreach() over the result set.
  *
- * 
- * All queries are passed as a prepared statement string.  A 
+ *
+ * All queries are passed as a prepared statement string.  A
  * prepared statement is a "template" of a query that omits literal or variable
- * values in favor of placeholders.  The values to place into those 
- * placeholders are passed separately, and the database driver handles 
+ * values in favor of placeholders.  The values to place into those
+ * placeholders are passed separately, and the database driver handles
  * inserting the values into the query in a secure fashion.  That means you
  * should never quote or string-escape a value to be inserted into the query.
- * 
+ *
  * There are two formats for placeholders: named and unnamed.  Named placeholders
- * are strongly preferred in all cases as they are more flexible and 
+ * are strongly preferred in all cases as they are more flexible and
  * self-documenting.
- * 
+ *
  * Named placeholders begin with a colon followed by a unique string. Example:
  * @code
  * SELECT nid, title FROM {node} WHERE uid=:uid
  * @endcode
- * 
+ *
  * ":uid" is a placeholder that will be replaced with a literal value when
  * the query is executed.  A given placeholder label cannot be repeated in a
  * given query, even if the value should be the same.  When using named
  * placeholders, the array of arguments to the query must be an associative
  * array where keys are a placeholder label (e.g., :uid) and the value is the
  * corresponding value to use.  The array may be in any order.
- * 
+ *
  * Unnamed placeholders are simply a question mark.  Example:
  * @code
  * SELECT nid, title FROM {node} WHERE uid=?
  * @endcode
- * 
+ *
  * In this case, the array of arguments must be an indexed array of values to
- * use in the exact same order as the placeholders in the query.  
- * 
+ * use in the exact same order as the placeholders in the query.
+ *
  * Note that placeholders should be a "complete" value.  For example, when
  * running a LIKE query the SQL wildcard character, %, should be part of the
  * value, not the query itself.  Thus, the following is incorrect:
- * 
+ *
  * @code
  * SELECT nid, title FROM {node} WHERE title LIKE :title%
  * @endcode
- * 
+ *
  * It should instead read:
- * 
+ *
  * @code
  * SELECT nid, title FROM {node} WHERE title LIKE :title
  * @endcode
- * 
+ *
  * and the value for :title should include a % as appropriate.  Again, note the
  * lack of quotation marks around :title.  Because the value is not inserted
  * into the query as one big string but as an explicitly separate value, the
- * database server knows where the query ends and a value begins.  That is 
- * considerably more secure against SQL injection than trying to remember 
+ * database server knows where the query ends and a value begins.  That is
+ * considerably more secure against SQL injection than trying to remember
  * which values need quotation marks and string escaping and which don't.
- * 
+ *
  *
  * INSERT, UPDATE, and DELETE queries need special care in order to behave
  * consistently across all different databases.  Therefore, they use a special
@@ -120,7 +120,7 @@ define('DB_ERROR', 'a515ac9c2796ca0e23ad
  * db_insert('my_table')->fields($fields)->execute();
  * @endcode
  * This method allows databases that need special data type handling to do so,
- * while also allowing optimizations such as multi-insert queries.  UPDATE and 
+ * while also allowing optimizations such as multi-insert queries.  UPDATE and
  * DELETE queries have a similar pattern.
  */
 
@@ -155,48 +155,48 @@ abstract class DatabaseConnection extend
   /**
    * Return the default query options for any given query.
    *
-   * A given query can be customized with a number of option flags in an 
+   * A given query can be customized with a number of option flags in an
    * associative array.
    *
-   *   target - The database "target" against which to execute a query.  Valid 
-   *   values are "default" or "slave".  The system will first try to open a 
-   *   connection to a database specified with the user-supplied key.  If one 
-   *   is not available, it will silently fall back to the "default" target.  
-   *   If multiple databases connections are specified with the same target, 
+   *   target - The database "target" against which to execute a query.  Valid
+   *   values are "default" or "slave".  The system will first try to open a
+   *   connection to a database specified with the user-supplied key.  If one
+   *   is not available, it will silently fall back to the "default" target.
+   *   If multiple databases connections are specified with the same target,
    *   one will be selected at random for the duration of the request.
-   * 
+   *
    *   fetch - This element controls how rows from a result set will be returned.
    *   legal values include PDO::FETCH_ASSOC, PDO::FETCH_BOTH, PDO::FETCH_OBJ,
    *   PDO::FETCH_NUM, or a string representing the name of a class.  If a string
    *   is specified, each record will be fetched into a new object of that class.
    *   The behavior of all other values is defined by PDO.  See
    *   http://www.php.net/PDOStatement-fetch
-   * 
+   *
    *   return - Depending on the type of query, different return values may be
    *   meaningful.  This directive instructs the system which type of return
-   *   value is desired.  The system will generally set the correct value 
+   *   value is desired.  The system will generally set the correct value
    *   automatically, so it is extremely rare that a module developer will ever
    *   need to specify this value.  Setting it incorrectly will likely lead to
    *   unpredictable results or fatal errors.  Legal values include:
-   *   
+   *
    *     Database::RETURN_STATEMENT - Return the prepared statement object for the
    *     query.  This is usually only meaningful for SELECT queries, where the
    *     statement object is how one accesses the result set returned by the query.
-   * 
-   *     Database::RETURN_AFFECTED - Return the number of rows affected by an 
-   *     UPDATE or DELETE query.  Be aware that means the number of rows 
+   *
+   *     Database::RETURN_AFFECTED - Return the number of rows affected by an
+   *     UPDATE or DELETE query.  Be aware that means the number of rows
    *     actually changed, not the number of rows matched by the WHERE clause.
-   *    
+   *
    *     Database::RETURN_INSERT_ID - Return the sequence ID (primary key)
    *     created by an INSERT statement on a table that contains a serial column.
-   * 
+   *
    *     Database::RETURN_NULL - Do not return anything, as there is no
    *     meaningful value to return.  That is the case for INSERT queries on
    *     tables that do not contain a serial column.
    *
-   *   throw_exception - By default, the database system will catch any errors 
-   *   on a query as an Exception, log it, and then rethrow it so that code 
-   *   further up the call chain can take an appropriate action.  To supress 
+   *   throw_exception - By default, the database system will catch any errors
+   *   on a query as an Exception, log it, and then rethrow it so that code
+   *   further up the call chain can take an appropriate action.  To supress
    *   that behavior and simply return NULL on failure, set this option to FALSE.
    *
    * @return
@@ -216,7 +216,7 @@ abstract class DatabaseConnection extend
    *
    * Queries sent to Drupal should wrap all table names in curly brackets. This
    * function searches for this syntax and adds Drupal's table prefix to all
-   * tables, allowing Drupal to coexist with other systems in the same database 
+   * tables, allowing Drupal to coexist with other systems in the same database
    * if necessary.
    *
    * @param $sql
@@ -250,18 +250,24 @@ abstract class DatabaseConnection extend
 
   /**
    * Prepare a query string and return the prepared statement.
-   * 
+   *
    * This method statically caches prepared statements, reusing them when
    * possible.  It also prefixes tables names enclosed in curly-braces.
    *
    * @param $query
    *   The query string as SQL, with curly-braces surrounding the
    *   table names.
+   * @param $reset
+   *   Reset the prepared statement static cache.
    * @return
    *   A PDO prepared statement ready for its execute() method.
    */
-  protected function prepareQuery($query) {
+  protected function prepareQuery($query, $reset = FALSE) {
     static $statements = array();
+    if ($reset) {
+      $statements = array();
+      return;
+    }
     $query = self::prefixTables($query);
     if (empty($statements[$query])) {
       $statements[$query] = parent::prepare($query);
@@ -271,10 +277,10 @@ abstract class DatabaseConnection extend
 
   /**
    * Create the appropriate sequence name for a given table and serial field.
-   * 
+   *
    * This information is exposed to all database drivers, although it is only
    * useful on some of them.  This method is table prefix-aware.
-   * 
+   *
    * @param $table
    *   The table name to use for the sequence.
    * @param $field
@@ -285,7 +291,7 @@ abstract class DatabaseConnection extend
   public function makeSequenceName($table, $field) {
     return $this->prefixTables('{'. $table .'}_'. $field .'_seq');
   }
-  
+
   /**
    * Executes a query string against the database.
    *
@@ -293,16 +299,16 @@ abstract class DatabaseConnection extend
    * of every query.  All queries executed by Drupal are executed as
    * PDO prepared statements.  This method statically caches those
    * prepared statements, reusing them when possible.
-   * 
+   *
    * @param $query
    *   The query to execute.  In most cases this will be a string containing
-   *   an SQL query with placeholders.  An already-prepared instance of 
+   *   an SQL query with placeholders.  An already-prepared instance of
    *   DatabaseStatement may also be passed in order to allow calling code
    *   to manually bind variables to a query.  If a DatabaseStatement object
    *   is passed, the $args array will be ignored.
-   * 
-   *   It is extremely rare that module code will need to pass a statement 
-   *   object to this method.  It is used primarily for database drivers for 
+   *
+   *   It is extremely rare that module code will need to pass a statement
+   *   object to this method.  It is used primarily for database drivers for
    *   databases that require special LOB field handling.
    * @param $args
    *   An array of arguments for the prepared statement.  If the prepared
@@ -323,7 +329,7 @@ abstract class DatabaseConnection extend
 
     // Use default values if not already set.
     $options += $this->defaultOptions();
-    
+
     try {
       // We allow either a pre-bound statement object or a literal string.
       // In either case, we want to end up with an executed statement object.
@@ -335,7 +341,7 @@ abstract class DatabaseConnection extend
         $stmt = $this->prepareQuery($query);
         $stmt->execute($args, $options);
       }
-      
+
       // Depending on the type of query we may need to return a different value.
       // See DatabaseConnection::defaultOptions() for a description of each value.
       switch ($options['return']) {
@@ -516,11 +522,11 @@ abstract class DatabaseConnection extend
    */
   public function startTransaction($required = FALSE) {
     static $class_type;
-    
+
     if ($required && !$this->supportsTransactions()) {
       throw new TransactionsNotSupportedException();
     }
-  
+
     if (empty($class_type)) {
       $class_type = 'DatabaseTransaction_' . $this->driver();
       if (!class_exists($class_type)) {
@@ -599,7 +605,7 @@ abstract class DatabaseConnection extend
    */
   abstract public function databaseType();
 
- 
+
   /**
    * Gets any special processing requirements for the condition operator.
    *
@@ -629,13 +635,13 @@ abstract class Database {
 
   /**
    * Flag to indicate a query call should simply return NULL.
-   * 
+   *
    * This is used for queries that have no reasonable return value
    * anyway, such as INSERT statements to a table without a serial
    * primary key.
    */
   const RETURN_NULL = 0;
-  
+
   /**
    * Flag to indicate a query call should return the prepared statement.
    */
@@ -645,12 +651,12 @@ abstract class Database {
    * Flag to indicate a query call should return the number of affected rows.
    */
   const RETURN_AFFECTED = 2;
-  
+
   /**
    * Flag to indicate a query call should return the "last insert id".
    */
   const RETURN_INSERT_ID = 3;
-  
+
   /**
    * An nested array of all active connections.  It is keyed by database name and target.
    *
@@ -712,7 +718,7 @@ abstract class Database {
    *   TRUE if there is at least one database connection established, FALSE otherwise.
    */
   final public static function isActiveConnection() {
-    return !empty(self::$connections);
+    return !empty(self::$activeKey) && !empty(self::$connections) && !empty(self::$connections[self::$activeKey]);
   }
 
   /**
@@ -735,7 +741,7 @@ abstract class Database {
 
   /**
    * Process the configuration file for database information.
-   * 
+   *
    * Because the config file accepts various "fallback" configurations, we have
    * to parse the configuration array out into a standardized "complete" form,
    * applying defaults where necessary.
@@ -760,8 +766,8 @@ abstract class Database {
       }
 
       foreach ($databaseInfo[$index] as $target => $value) {
-        // If there is no "driver" property, then we assume it's an array of 
-        // possible connections for this target.  Pick one at random.  That 
+        // If there is no "driver" property, then we assume it's an array of
+        // possible connections for this target.  Pick one at random.  That
         // allows us to have, for example, multiple slave servers.
         if (empty($value['driver'])) {
           $databaseInfo[$index][$target] = $databaseInfo[$index][$target][mt_rand(0, count($databaseInfo[$index][$target]) - 1)];
@@ -771,7 +777,7 @@ abstract class Database {
 
     self::$databaseInfo = $databaseInfo;
   }
-  
+
   /**
    * Gets information on the specified database connection.
    *
@@ -786,7 +792,7 @@ abstract class Database {
     if (!empty(self::$databaseInfo[$key])) {
       return self::$databaseInfo[$key];
     }
-    
+
   }
 
   /**
@@ -797,7 +803,7 @@ abstract class Database {
    *   is "default".
    * @param $target
    *   The database target to open.  If the specified target does not exist,
-   *   the "default" target will be used instead.  
+   *   the "default" target will be used instead.
    */
   final protected static function openConnection($key, $target) {
     global $db_prefix;
@@ -821,7 +827,7 @@ abstract class Database {
       if (!$driver = self::$databaseInfo[$key][$target]['driver']) {
         throw new Exception('Drupal is not set up');
       }
-      
+
       // We cannot rely on the registry yet, because the registry requires
       // an open database connection.
       $driver_class = 'DatabaseConnection_' . $driver;
@@ -847,8 +853,8 @@ abstract class Database {
 
 /**
  * Exception to mark databases that do not support transations.
- * 
- * This exception will be thrown when a transaction is started that does not 
+ *
+ * This exception will be thrown when a transaction is started that does not
  * allow for the "silent fallback" of no transaction and the database connection
  * in use does not support transactions.  The calling code must then take
  * appropriate action.
@@ -908,7 +914,7 @@ class DatabaseTransaction {
 
   /**
    * Track the number of "layers" of transactions currently active.
-   * 
+   *
    * On many databases transactions cannot nest.  Instead, we track
    * nested calls to transactions and collapse them into a single
    * transaction.
@@ -916,7 +922,7 @@ class DatabaseTransaction {
    * @var int
    */
   protected static $layers = 0;
-  
+
   public function __construct(DatabaseConnection $connection) {
     $this->connection = $connection;
     $this->supportsTransactions = $connection->supportsTransactions();
@@ -924,7 +930,7 @@ class DatabaseTransaction {
     if (self::$layers == 0 && $this->supportsTransactions) {
       $connection->beginTransaction();
     }
-    
+
     ++self::$layers;
   }
 
@@ -971,9 +977,9 @@ class DatabaseTransaction {
 /**
  * Prepared statement class.
  *
- * PDO allows us to extend the PDOStatement class to provide additional 
- * functionality beyond that offered by default.  We do need extra 
- * functionality.  By default, this class is not driver-specific.  If a given 
+ * PDO allows us to extend the PDOStatement class to provide additional
+ * functionality beyond that offered by default.  We do need extra
+ * functionality.  By default, this class is not driver-specific.  If a given
  * driver needs to set a custom statement class, it may do so in its constructor.
  *
  * @link http://us.php.net/pdostatement
@@ -982,7 +988,7 @@ class DatabaseStatement extends PDOState
 
   /**
    * Reference to the database connection object for this statement.
-   * 
+   *
    * The name $dbh is inherited from PDOStatement.
    *
    * @var DatabaseConnection
@@ -1033,8 +1039,8 @@ class DatabaseStatement extends PDOState
 
   /**
    * Returns an entire result set as an associative array keyed by the named field.
-   * 
-   * If the given key appears multiple times, later records will overwrite 
+   *
+   * If the given key appears multiple times, later records will overwrite
    * earlier ones.
    *
    * Note that this method will run the result set to the end.
@@ -1042,7 +1048,7 @@ class DatabaseStatement extends PDOState
    * @param $key
    *   The name of the field on which to index the array.
    * @param $fetch
-   *   The fetchmode to use.  If set to   PDO::FETCH_ASSOC, PDO::FETCH_NUM, or 
+   *   The fetchmode to use.  If set to   PDO::FETCH_ASSOC, PDO::FETCH_NUM, or
    *   PDO::FETCH_BOTH the returned value with be an array of arrays.  For any
    *   other value it will be an array of objects.
    * @return
@@ -1150,7 +1156,7 @@ function db_query($query, $args = array(
     array_shift($args);
   }
   list($query, $args, $options) = _db_query_process_args($query, $args, $options);
-  
+
   return Database::getActiveConnection($options['target'])->query($query, $args, $options);
 }
 
@@ -1184,7 +1190,7 @@ function db_query_range($query, $args, $
     $from = array_pop($args);
   }
   list($query, $args, $options) = _db_query_process_args($query, $args, $options);
-  
+
   return Database::getActiveConnection($options['target'])->queryRange($query, $args, $from, $count, $options);
 }
 
@@ -1214,7 +1220,7 @@ function db_query_temporary($query, $arg
     array_shift($args);
   }
   list($query, $args, $options) = _db_query_process_args($query, $args, $options);
-  
+
   return Database::getActiveConnection($options['target'])->queryTemporary($query, $args, $tablename, $options);
 }
 
@@ -1356,7 +1362,9 @@ function db_escape_table($table) {
  *     query: the SQL query executed, passed through check_plain()
  */
 function update_sql($sql) {
-  $result = Database::getActiveConnection()->query($sql/*, array(true)*/);
+  $activeConnection = Database::getActiveConnection();
+  $result = $activeConnection->query($sql);
+  $activeConnection->prepareQuery('', TRUE);
   return array('success' => $result !== FALSE, 'query' => check_plain($sql));
 }
 
@@ -1598,7 +1606,7 @@ function db_drop_field(&$ret, $table, $f
  *   Default value to be set. NULL for 'default NULL'.
  */
 function db_field_set_default(&$ret, $table, $field, $default) {
-  return Database::getActiveConnection()->schema()->dropField($ret, $table, $field, $default);
+  return Database::getActiveConnection()->schema()->fieldSetDefault($ret, $table, $field, $default);
 }
 
 /**
@@ -1815,11 +1823,11 @@ function _db_need_install() {
 
 /**
  * Backward-compatibility utility.
- * 
+ *
  * This function should be removed after all queries have been converted
  * to the new API.  It is temporary only.
- * 
- * @todo Remove this once the query conversion is complete. 
+ *
+ * @todo Remove this once the query conversion is complete.
  */
 function _db_query_process_args($query, $args, $options) {
 
@@ -1829,7 +1837,7 @@ function _db_query_process_args($query, 
   if (empty($options['target'])) {
     $options['target'] = 'default';
   }
-  
+
   // Temporary backward-compatibliity hacks.  Remove later.
   $old_query = $query;
   $query = str_replace(array('%n', '%d', '%f', '%b', "'%s'", '%s'), '?', $old_query);

=== modified file 'includes/database/schema.inc'
--- includes/database/schema.inc	2008-08-21 19:36:35 +0000
+++ includes/database/schema.inc	2008-08-28 20:33:39 +0000
@@ -117,29 +117,29 @@
  */
 
 abstract class DatabaseSchema {
-  
+
   protected $connection;
-  
+
   public function __construct($connection) {
     $this->connection = $connection;
   }
-  
+
   /**
    * Check if a table exists.
    */
   abstract public function tableExists($table);
-  
+
   /**
    * Check if a column exists in the given table.
    */
   abstract public function columnExists($table, $column);
-  
+
   /**
    * This maps a generic data type in combination with its data size
    * to the engine-specific data type.
    */
   abstract public function getFieldTypeMap();
-  
+
   /**
    * Rename a table.
    *
@@ -161,7 +161,7 @@ abstract class DatabaseSchema {
    *   The table to be dropped.
    */
   abstract public function dropTable(&$ret, $table);
-  
+
   /**
    * Add a new field to a table.
    *
@@ -380,7 +380,7 @@ abstract class DatabaseSchema {
       $ret[] = update_sql($statement);
     }
   }
-  
+
   /**
    * Return an array of field names from an array of key/index column specifiers.
    *

=== added directory 'includes/database/sqlite'
=== added file 'includes/database/sqlite/database.inc'
--- includes/database/sqlite/database.inc	1970-01-01 00:00:00 +0000
+++ includes/database/sqlite/database.inc	2008-08-30 21:24:12 +0000
@@ -0,0 +1,66 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Database interface code for SQLite embedded database engine.
+ */
+
+/**
+ * @ingroup database
+ * @{
+ */
+
+class DatabaseConnection_sqlite extends DatabaseConnection {
+  public function __construct(Array $connection_options = array()) {
+    parent::__construct('sqlite:'. $connection_options['database'], '', '');
+  }
+
+  public function queryRange($query, Array $args, $from, $count, Array $options) {
+    // Backward compatibility hack, temporary.
+    $query = str_replace(array('%d' , '%f' , '%b' , "'%s'"), '?', $query);
+
+    return $this->query($query . ' LIMIT ' . $from . ', ' . $count, $args, $options);
+  }
+
+  public function queryTemporary($query, Array $args, $tablename) {
+    $query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' SELECT', $this->prefixTables($query));
+
+    return $this->query($query, $args, $options);
+  }
+
+  public function driver() {
+    return 'sqlite';
+  }
+
+  public function databaseType() {
+    return 'sqlite';
+  }
+
+  public function supportsTransactions() {
+    return TRUE;
+  }
+
+  public function escapeTable($table) {
+    return preg_replace('/[^A-Za-z0-9_]+/', '', $table);
+  }
+
+  public function mapConditionOperator($operator) {
+    // We don't want to override any of the defaults.
+    return NULL;
+  }
+
+  /**
+   * @todo Remove this as soon as db_rewrite_sql() has been exterminated.
+   */
+  public function distinctField($table, $field, $query) {
+    $field_to_select = 'DISTINCT(' . $table . '.' . $field . ')';
+    // (?<!text) is a negative look-behind (no need to rewrite queries that already use DISTINCT).
+    return preg_replace('/(SELECT.*)(?:' . $table . '\.|\s)(?<!DISTINCT\()(?<!DISTINCT\(' . $table . '\.)' . $field . '(.*FROM )/AUsi', '\1 ' . $field_to_select . '\2', $query);
+  }
+}
+
+
+/**
+ * @} End of "ingroup database".
+ */

=== added file 'includes/database/sqlite/install.inc'
--- includes/database/sqlite/install.inc	1970-01-01 00:00:00 +0000
+++ includes/database/sqlite/install.inc	2008-08-24 19:15:49 +0000
@@ -0,0 +1,12 @@
+<?php
+// $Id$
+
+// SQLite specific install functions
+
+class DatabaseInstaller_sqlite extends DatabaseInstaller {
+  protected $pdoDriver = 'sqlite';
+  public function name() {
+    return 'SQLite';
+  }
+}
+

=== added file 'includes/database/sqlite/query.inc'
--- includes/database/sqlite/query.inc	1970-01-01 00:00:00 +0000
+++ includes/database/sqlite/query.inc	2008-08-25 20:28:55 +0000
@@ -0,0 +1,119 @@
+<?php
+// $Id: query.inc,v 1.1 2008/08/21 19:36:36 dries Exp $
+
+/**
+ * @ingroup database
+ * @{
+ */
+
+class InsertQuery_sqlite extends InsertQuery {
+
+  public function execute() {
+
+    // Confirm that the user did not try to specify an identical
+    //  field and default field.
+    if (array_intersect($this->insertFields, $this->defaultFields)) {
+      throw new PDOException('You may not specify the same field to have a value and a schema-default value.');
+    }
+
+    $last_insert_id = 0;
+
+    $max_placeholder = 0;
+    $values = array();
+    foreach ($this->insertValues as $insert_values) {
+      foreach ($insert_values as $value) {
+        $values[':db_insert_placeholder_' . $max_placeholder++] = $value;
+      }
+    }
+
+    $last_insert_id = $this->connection->query((string)$this, $values, $this->queryOptions);
+
+    // Re-initialize the values array so that we can re-use this query.
+    $this->insertValues = array();
+
+    return $last_insert_id;
+  }
+
+  public function __toString() {
+
+    // Default fields are always placed first for consistency.
+    $insert_fields = array_merge($this->defaultFields, $this->insertFields);
+
+    $query = "INSERT INTO {" . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES ';
+
+    $max_placeholder = 0;
+    $values = array();
+    if (count($this->insertValues)) {
+      foreach ($this->insertValues as $insert_values) {
+        $placeholders = array();
+
+        // Default fields aren't really placeholders, but this is the most convenient
+        // way to handle them.
+        $placeholders = array_pad($placeholders, count($this->defaultFields), 'default');
+
+        $new_placeholder = $max_placeholder + count($insert_values);
+        for ($i = $max_placeholder; $i < $new_placeholder; ++$i) {
+          $placeholders[] = ':db_insert_placeholder_'. $i;
+        }
+        $max_placeholder = $new_placeholder;
+        $values[] = '('. implode(', ', $placeholders) .')';
+      }
+    }
+    else {
+      // If there are no values, then this is a default-only query.  We still need to handle that.
+      $placeholders = array_fill(0, count($this->defaultFields), 'default');
+      $values[] = '(' . implode(', ', $placeholders) .')';
+    }
+
+    $query .= implode(', ', $values);
+
+    return $query;
+  }
+}
+
+class MergeQuery_sqlite extends MergeQuery {
+
+  public function execute() {
+    if ($this->expressionFields || $this->updateFields || $this->excludeFields) {
+      return parent::execute();
+    }
+
+    $insert_fields = $this->insertFields + $this->keyFields;
+
+    $max_placeholder = 0;
+    $values = array();
+    // We assume that the order here is the same as in __toString().  If that's
+    // not the case, then we have serious problems.
+    foreach ($insert_fields as $value) {
+      $values[':db_insert_placeholder_' . $max_placeholder++] = $value;
+    }
+
+    $last_insert_id = $this->connection->query((string)$this, $values, $this->queryOptions);
+
+    return $last_insert_id;
+  }
+
+
+  public function __toString() {
+    if ($this->expressionFields || $this->updateFields || $this->excludeFields) {
+      return parent::_toString();
+    }
+
+    $insert_fields = $this->insertFields + $this->keyFields;
+
+    $query = "INSERT OR REPLACE INTO {" . $this->table . '} (' . implode(', ', array_keys($insert_fields)) . ') VALUES ';
+
+    $max_placeholder = 0;
+    $values = array();
+    // We don't need the $field, but this is a convenient way to count.
+    foreach ($insert_fields as $field) {
+      $values[] = ':db_insert_placeholder_' . $max_placeholder++;
+    }
+    $query .= '(' . implode(', ', $values) . ')';
+    return $query;
+  }
+}
+
+/**
+ * @} End of "ingroup database".
+ */

=== added file 'includes/database/sqlite/schema.inc'
--- includes/database/sqlite/schema.inc	1970-01-01 00:00:00 +0000
+++ includes/database/sqlite/schema.inc	2008-08-30 21:17:35 +0000
@@ -0,0 +1,513 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Database schema code for SQLite database servers.
+ */
+
+
+/**
+ * @ingroup schemaapi
+ * @{
+ */
+
+class DatabaseSchema_sqlite extends DatabaseSchema {
+
+  public function tableExists($table) {
+    return (bool) $this->connection->query("SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE '{" . $table . "}'", array(), array())->fetchField();
+  }
+
+  public function columnExists($table, $column) {
+    $schema = $this->introspectSchema($table);
+    return $schema['fields'][$column];
+  }
+
+  /**
+   * Generate SQL to create a new table from a Drupal schema definition.
+   *
+   * @param $name
+   *   The name of the table to create.
+   * @param $table
+   *   A Schema API table definition array.
+   * @return
+   *   An array of SQL statements to create the table.
+   */
+  public function createTableSql($name, $table) {
+    $sql = array();
+    $sql[] = "CREATE TABLE {". $name ."} (\n". $this->buildColsSql($name, $table) ."\n);\n";
+    return array_merge($sql, $this->createIndexSql($name, $table));
+  }
+
+  protected function createIndexSql($tablename, $schema) {
+    $sql = array();
+    if (!empty($schema['unique keys'])) {
+      foreach ($schema['unique keys'] as $key => $fields) {
+        $sql[] = 'CREATE UNIQUE INDEX {'. $tablename .'}_'. $key .' ON {'. $tablename .'} ('. $this->createKeySql($fields) ."); \n";
+      }
+    }
+    if (!empty($schema['indexes'])) {
+      foreach ($schema['indexes'] as $index => $fields) {
+        $sql[] = 'CREATE INDEX {'. $tablename .'}_'. $index .' ON {'. $tablename .'} ('. $this->createKeySql($fields) ."); \n";
+      }
+    }
+    return $sql;
+  }
+
+  protected function buildColsSql($tablename, $schema) {
+    $sql = "";
+
+    // Add the SQL statement for each field.
+    foreach ($schema['fields'] as $name => $field) {
+      if ($field['type'] == 'serial') {
+        if (isset($schema['primary key']) && ($key = array_search($name, $schema['primary key'])) !== false) {
+          unset($schema['primary key'][$key]);
+        }
+      }
+      $sql .= $this->createFieldSql($name, $this->processField($field)) .", \n";
+    }
+
+    // Process keys.
+    if (!empty($schema['primary key'])) {
+      $sql .= " PRIMARY KEY (". $this->createKeySql($schema['primary key']) ."), \n";
+    }
+
+    // Remove the last comma and space.
+    $sql = substr($sql, 0, -3);
+    return $sql;
+  }
+
+  protected function createKeySql($fields) {
+    $ret = array();
+    foreach ($fields as $field) {
+      if (is_array($field)) {
+        $ret[] = $field[0];
+      }
+      else {
+        $ret[] = $field;
+      }
+    }
+    return implode(', ', $ret);
+  }
+
+  /**
+   * Set database-engine specific properties for a field.
+   *
+   * @param $field
+   *   A field description array, as specified in the schema documentation.
+   */
+  protected function processField($field) {
+    if (!isset($field['size'])) {
+      $field['size'] = 'normal';
+    }
+    // Set the correct database-engine specific datatype.
+    if (!isset($field['sqlite_type'])) {
+      $map = db_type_map();
+      $field['sqlite_type'] = $map[$field['type'] .':'. $field['size']];
+    }
+
+    if ($field['type'] == 'serial') { // this should check if field is a primary key
+      $field['auto_increment'] = TRUE;
+    }
+
+    return $field;
+  }
+
+  /**
+  * Create an SQL string for a field to be used in table creation or alteration.
+  *
+  * Before passing a field out of a schema definition into this function it has
+  * to be processed by db_processField().
+  *
+  * @param $name
+  *    Name of the field.
+  * @param $spec
+  *    The field specification, as per the schema data structure format.
+  */
+  protected function createFieldSql($name, $spec) {
+    if (!empty($spec['auto_increment'])) {
+      $sql = "". $name ." INTEGER PRIMARY KEY";
+    }
+    else {
+      $sql = "". $name ." ". $spec['sqlite_type'];
+
+      if (isset($spec['length'])) {
+        $sql .= '('. $spec['length'] .')';
+      }
+      //TODO: does scale apply for sqlite ??
+      elseif (isset($spec['precision']) && isset($spec['scale'])) {
+        $sql .= '('. $spec['scale'] .', '. $spec['precision'] .')';
+      }
+
+      if (!empty($spec['not null'])) {
+        $sql .= ' NOT NULL';
+      }
+
+      if (isset($spec['default'])) {
+        if (is_string($spec['default'])) {
+          $spec['default'] = "'". $spec['default'] ."'";
+        }
+        $sql .= ' DEFAULT '. $spec['default'];
+      }
+
+      if (empty($spec['not null']) && !isset($spec['default'])) {
+        $sql .= ' DEFAULT NULL';
+      }
+    }
+    return $sql;
+  }
+
+  /**
+   * This maps a generic data type in combination with its data size
+   * to the engine-specific data type.
+   */
+  public function getFieldTypeMap() {
+    // Put :normal last so it gets preserved by array_flip.  This makes
+    // it much easier for modules (such as schema.module) to map
+    // database types back into schema types.
+    /*
+    a VARCHAR(10),
+    b NVARCHAR(15),
+    c TEXT,
+    d INTEGER,
+    e FLOAT,
+    f BOOLEAN,
+    g CLOB,
+    h BLOB,
+    i TIMESTAMP,
+    j NUMERIC(10,5)
+    k VARYING CHARACTER (24),
+    l NATIONAL VARYING CHARACTER(16)
+    */
+    $map = array(
+      'varchar:normal'  => 'VARCHAR',
+
+      'text:tiny'       => 'TEXT',
+      'text:small'      => 'TEXT',
+      'text:medium'     => 'TEXT',
+      'text:big'        => 'TEXT',
+      'text:normal'     => 'TEXT',
+
+      'serial:tiny'     => 'INTEGER',
+      'serial:small'    => 'INTEGER',
+      'serial:medium'   => 'INTEGER',
+      'serial:big'      => 'INTEGER',
+      'serial:normal'   => 'INTEGER',
+
+      'int:tiny'        => 'INTEGER',
+      'int:small'       => 'INTEGER',
+      'int:medium'      => 'INTEGER',
+      'int:big'         => 'INTEGER',
+      'int:normal'      => 'INTEGER',
+
+      'float:tiny'      => 'FLOAT',
+      'float:small'     => 'FLOAT',
+      'float:medium'    => 'FLOAT',
+      'float:big'       => 'FLOAT',
+      'float:normal'    => 'FLOAT',
+
+      'numeric:normal'  => 'NUMERIC',
+
+      'blob:big'        => 'BLOB',
+      'blob:normal'     => 'BLOB',
+
+      'datetime:normal' => 'TIMESTAMP',
+    );
+    return $map;
+  }
+
+  /**
+  * Rename a table.
+  *
+  * @param $ret
+  *   Array to which query results will be added.
+  * @param $table
+  *   The table to be renamed.
+  * @param $new_name
+  *   The new name for the table.
+  */
+  public function renameTable(&$ret, $table, $new_name) {
+    $ret[] = update_sql('ALTER TABLE {'. $table .'} RENAME TO {'. $new_name .'}');
+  }
+
+  /**
+   * Drop a table.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be dropped.
+   */
+  public function dropTable(&$ret, $table) {
+    $ret[] = update_sql('DROP TABLE {' . $table . '}');
+  }
+
+  /**
+   * Add a new field to a table.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   Name of the table to be altered.
+   * @param $field
+   *   Name of the field to be added.
+   * @param $spec
+   *   The field specification array, as taken from a schema definition
+   */
+  public function addField(&$ret, $table, $field, $spec, $keys_new = array()) {
+    // TODO: keys_new
+    $query = 'ALTER TABLE {'. $table .'} ADD ';
+    $query .= $this->createFieldSql($field, $this->processField($spec));
+    $ret[] = update_sql($query);
+  }
+
+  /**
+   * Recreate a table with a new schema with the old content.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   Name of the table to be altered.
+   * @param $new_schema
+   *   The new schema array for the table.
+   */
+  protected function createNewTableWithOldData(&$ret, $table, $new_schema) {
+    $i = 0;
+    do {
+      $new_table = $table . '_' . $i++;
+    } while ($this->tableExists($new_table));
+    $this->createTable($ret, $new_table, $new_schema);
+    $fields = implode(', ', array_keys($new_schema['fields']));
+    $ret[] = update_sql('INSERT INTO {'. $new_table ."} ($fields) SELECT $fields FROM {". $table . '}');
+    $old_count = db_query('SELECT COUNT(*) FROM {'. $table .'}')->fetchField;
+    $new_count = db_query('SELECT COUNT(*) FROM {'. $new_table .'}')->fetchField;
+    if ($old_count == $new_count) {
+      do {
+        $temp_table = $table . '_' . $i++;
+      } while ($this->tableExists($temp_table));
+      $this->renameTable($ret, $table, $temp_table);
+      $this->renameTable($ret, $new_table, $table);
+      $this->dropTable($ret, $temp_table);
+    }
+  }
+
+  protected function introspectSchema($table) {
+    $schema = array();
+    foreach (db_query("PRAGMA table_info('{" . $table . "}')") as $row) {
+      $schema['fields'][$row->name] = array(
+        'type' => $row->type,
+        'not null' => !empty($row->notnull),
+        'default' => trim($row->dflt_value, "'"),
+      );
+      if ($row->pk) {
+        $schema['primary key'][] = $row->name;
+      }
+    }
+    $indexes = array();
+    foreach (db_query("PRAGMA index_list('{" . $table . "}')") as $row) {
+      if (strpos($row->name, 'sqlite_autoindex_') !== 0) {
+        $indexes[] = array(
+          'schema_key' => $row->unique ? 'unique keys' : 'indexes',
+          'name' => $row->name,
+        );
+      }
+    }
+    $n = strlen($table) + 1;
+    foreach ($indexes as $index) {
+      $name = $index['name'];
+      $index_name = substr($name, $n);
+      foreach (db_query("PRAGMA index_info('$name')") as $row) {
+        $schema[$index['schema_key']][$index_name][] = $row->name;
+      }
+    }
+    return $schema;
+  }
+
+  /**
+   * Drop a field.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be altered.
+   * @param $field
+   *   The field to be dropped.
+   */
+  public function dropField(&$ret, $table, $field) {
+    $new_schema = $this->introspectSchema($table);
+    unset($new_schema['fields'][$field]);
+    foreach ($new_schema['indexes'] as $index => $fields) {
+      foreach ($fields as $key => $field_name) {
+        if ($field_name == $field) {
+          unset($new_schema['indexes'][$index][$key]);
+        }
+      }
+      if (empty($new_schema['indexes'][$index])) {
+        unset($new_schema['indexes'][$index]);
+      }
+    }
+    $this->createNewTableWithOldData($ret, $table, $new_schema);
+  }
+
+  /**
+   * Change a field definition.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   Name of the table.
+   * @param $field
+   *   Name of the field to change.
+   * @param $field_new
+   *   New name for the field (set to the same as $field if you don't want to change the name).
+   * @param $spec
+   *   The field specification for the new field.
+   * @param $keys_new
+   *   Optional keys and indexes specification to be created on the
+   *   table along with changing the field. The format is the same as a
+   *   table specification but without the 'fields' element.
+   */
+  public function changeField(&$ret, $table, $field, $field_new, $spec, $keys_new = array()) {
+    $new_schema = $this->introspectSchema($table);
+    unset($new_schema['fields'][$field]);
+    $new_schema['fields'][$field_new] = $spec;
+    if (isset($keys_new['primary keys'])) {
+      $new_schema['primary keys'] = $keys_new['primary keys'];
+      $keys_new['primary keys'];
+    }
+    foreach (array('unique keys', 'indexes') as $k) {
+      if (!empty($keys_new[$k])) {
+        $new_schema[$k] = $keys_new[$k] + $new_schema[$k];
+      }
+    }
+    $this->createNewTableWithOldData($ret, $table, $new_schema);
+  }
+
+  /**
+   * Add an index.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be altered.
+   * @param $name
+   *   The name of the index.
+   * @param $fields
+   *   An array of field names.
+   */
+  public function addIndex(&$ret, $table, $name, $fields) {
+    $schema['indexes'][$name] = $fields;
+    $ret[] = update_sql($this->createIndexSql($table, $schema));
+  }
+
+  /**
+   * Drop an index.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be altered.
+   * @param $name
+   *   The name of the index.
+   */
+  public function dropIndex(&$ret, $table, $name) {
+    $ret[] = update_sql('DROP INDEX ' . '{' . $table . '}_' . $name);
+  }
+
+  /**
+   * Add a unique key.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be altered.
+   * @param $name
+   *   The name of the key.
+   * @param $fields
+   *   An array of field names.
+   */
+  public function addUniqueKey(&$ret, $table, $name, $fields) {
+    $schema['unique keys'][$name] = $fields;
+    $ret[] = update_sql($this->createIndexSql($table, $schema));
+
+  }
+
+  /**
+   * Drop a unique key.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be altered.
+   * @param $name
+   *   The name of the key.
+   */
+  public function dropUniqueKey(&$ret, $table, $name) {
+    $ret[] = update_sql('DROP INDEX ' . '{' . $table . '}_' . $name);
+  }
+
+  /**
+   * Add a primary key.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be altered.
+   * @param $fields
+   *   Fields for the primary key.
+   */
+  public function addPrimaryKey(&$ret, $table, $fields) {
+    $new_schema = $this->introspectSchema($table);
+    $new_schema['primary key'] = $fields;
+    $this->createNewTableWithOldData($ret, $table, $new_schema);
+  }
+
+  /**
+   * Drop the primary key.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be altered.
+   */
+  public function dropPrimaryKey(&$ret, $table) {
+    $new_schema = $this->introspectSchema($table);
+    unset($new_schema['primary key']);
+    $this->createNewTableWithOldData($ret, $table, $new_schema);
+  }
+
+  /**
+   * Set the default value for a field.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be altered.
+   * @param $field
+   *   The field to be altered.
+   * @param $default
+   *   Default value to be set. NULL for 'default NULL'.
+   */
+  public function fieldSetDefault(&$ret, $table, $field, $default) {
+    $new_schema = $this->introspectSchema($table);
+    $new_schema['fields'][$field]['default'] = $default;
+    $this->createNewTableWithOldData($ret, $table, $new_schema);
+  }
+
+  /**
+   * Set a field to have no default value.
+   *
+   * @param $ret
+   *   Array to which query results will be added.
+   * @param $table
+   *   The table to be altered.
+   * @param $field
+   *   The field to be altered.
+   */
+  public function fieldSetNoDefault(&$ret, $table, $field) {
+    $new_schema = $this->introspectSchema($table);
+    unset($new_schema['fields'][$field]['default']);
+    $this->createNewTableWithOldData($ret, $table, $new_schema);
+  }
+
+}
\ No newline at end of file

=== modified file 'install.php'
--- install.php	2008-08-21 19:36:35 +0000
+++ install.php	2008-08-25 08:07:25 +0000
@@ -261,7 +261,6 @@ function install_settings_form(&$form_st
       '#default_value' => empty($database['username']) ? '' : $database['username'],
       '#size' => 45,
       '#maxlength' => 45,
-      '#required' => TRUE,
     );
 
     // Database username

=== modified file 'modules/simpletest/drupal_web_test_case.php'
--- modules/simpletest/drupal_web_test_case.php	2008-08-23 07:42:54 +0000
+++ modules/simpletest/drupal_web_test_case.php	2008-08-30 21:22:37 +0000
@@ -72,12 +72,12 @@ class DrupalWebTestCase {
     $db_prefix = $this->db_prefix_original;
     db_insert('simpletest')->fields(array(
       'test_id' => $this->test_id,
-    'test_class' => get_class($this), 
-    'status' => $status, 
+    'test_class' => get_class($this),
+    'status' => $status,
     'message' => substr($message, 0, 255),  // Some messages are too long for the database.
-    'message_group' => $group, 
-    'caller' => $function['function'], 
-    'line' => $function['line'], 
+    'message_group' => $group,
+    'caller' => $function['function'],
+    'line' => $function['line'],
     'file' => $function['file'],
     ))->execute();
     $this->_assertions[] = array(
@@ -405,7 +405,7 @@ class DrupalWebTestCase {
     node_types_rebuild();
 
     $this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type)));
-    
+
     // Reset permissions so that permissions for this content type are available.
     $this->checkPermissions(array(), TRUE);
 
@@ -645,7 +645,7 @@ class DrupalWebTestCase {
 
     // Generate temporary prefixed database to ensure that tests have a clean starting point.
     $db_prefix = 'simpletest' . mt_rand(1000, 1000000);
-    
+
     include_once './includes/install.inc';
     drupal_install_system();
 
@@ -659,7 +659,7 @@ class DrupalWebTestCase {
     // stale data for the previous run's database prefix and all
     // calls to it will fail.
     drupal_get_schema(NULL, TRUE);
-    
+
     // Run default profile tasks.
     $task = 'profile';
     default_profile_tasks($task, '');

=== added file 'modules/simpletest/tests/schema.test'
--- modules/simpletest/tests/schema.test	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/schema.test	2008-08-30 21:23:01 +0000
@@ -0,0 +1,76 @@
+<?php
+// $Id$
+
+class SchemaTestCase extends DrupalWebTestCase {
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Schema API'),
+      'description' => t('Tests table creation and modification via the schema API.'),
+      'group' => t('System'),
+    );
+  }
+
+  /**
+   *
+   */
+  function testSchema() {
+    $table_name = 'test';
+    $table_specification = array(
+      'fields' => array(
+        'id'  => array(
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => NULL,
+        ),
+        'test'  => array(
+          'type' => 'int',
+          'not null' => TRUE,
+        ),
+      ),
+    );
+    $ret = array();
+    db_create_table($ret, $table_name, $table_specification);
+    $this->assertTrue(db_table_exists($table_name), t('The table exists.'));
+    $field_name = 'test';
+    $this->assertFalse($this->tryInsert(), t('Insert without a default failed.'));
+    db_field_set_default($ret, $table_name, $field_name, 0);
+    $this->assertTrue($this->tryInsert(), t('Insert with a default succeeded.'));
+    db_field_set_no_default($ret, $table_name, $field_name);
+    $this->assertFalse($this->tryInsert(), t('Insert without a default failed.'));
+    db_rename_table($ret, $table_name, $table_name . '_test');
+    // We need the default so that we can insert after the rename.
+    db_field_set_default($ret, $table_name . '_test', $field_name, 0);
+    $this->assertFalse($this->tryInsert(), t('Insert into the old table failed.'));
+    $this->assertTrue($this->tryInsert('_test'), t('Insert into the new table succeeded.'));
+    db_drop_table($ret, $table_name . '_test');
+    $this->assertFalse(db_table_exists($table_name . '_test'), t('The renamed table does not exist.'));
+    db_create_table($ret, $table_name, $table_specification);
+    db_field_set_default($ret, $table_name, $field_name, 0);
+    db_add_field($ret, $table_name, 'test2', array('type' => 'init', 'not null' => TRUE, 'default' => 0));
+    db_change_field($ret, $table_name, 'test2', 'test2', array('type' => 'serial', 'not null' => TRUE), array('primary key' => array('test2')));
+    $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.'));
+    $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.'));
+    $max = 0;
+    $counter = 0;
+    foreach (db_query('SELECT test2 FROM {test} ORDER BY test2') as $row) {
+      $counter++;
+      $this->assertTrue(!$max || $row->test2 > $max, t('The serial is monotone.'));
+      $max = $row->test2;
+    }
+    $this->assertTrue($max, t('There was a non zero serial.'));
+    $this->assertEqual($counter, 2, t('There were two rows.'));
+  }
+
+  function tryInsert($tablepostfix = '') {
+    try {
+      db_query("INSERT INTO {test$tablepostfix} (id) VALUES (:id)", mt_rand(10,20));
+      return TRUE;
+    }
+    catch (Exception $e) {
+      return FALSE;
+    }
+  }
+}
\ No newline at end of file

