diff -urpN prepatch-dev/includes/database.db2.inc drupal-6.x-dev-db2-0.4/includes/database.db2.inc
--- prepatch-dev/includes/database.db2.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-db2-0.4/includes/database.db2.inc	2007-08-09 12:25:41.000000000 +0800
@@ -0,0 +1,1188 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Database interface code for DB2 database servers.
+ */
+
+/**
+ * @ingroup database
+ * @{
+ */
+
+/**
+ * Report database status.
+ */
+function db_status_report() {
+  $t = get_t();
+
+  $version = db_version();
+
+  $form['db2'] = array(
+    'title' => $t('DB2 database'),
+    'value' => $version,
+  );
+  return $form;
+}
+
+/**
+ * Returns the version of the database server currently in use.
+ *
+ * @return Database server version
+ */
+function db_version() {
+  return db_result(db_query("SELECT SERVICE_LEVEL FROM TABLE (sysproc.env_get_inst_info()) as INSTANCEINFO"));
+}
+
+/**
+ * Initialize a database connection.
+ */
+function db_connect($url) {
+  // Check if DB2 support is present in PHP
+  if (!function_exists('db2_connect')) {
+    drupal_maintenance_theme();
+    drupal_set_title('PHP DB2 support not enabled');
+    print theme('maintenance_page', '<p>We were unable to use the DB2 database because the DB2 extension for PHP is not installed. Check your <code>PHP.ini</code> to see how you can enable it.</p>
+<p>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.</p>');
+    exit;
+  }
+
+  $url = parse_url($url);
+
+  // Decode url-encoded information in the db connection string.
+  $conn_string['database'] = 'DATABASE=' . substr(urldecode($url['path']), 1);
+  $conn_string['hostname'] = 'HOSTNAME=' . urldecode($url['host']);
+  $conn_string['port'] = isset($url['port']) ? 'PORT=' . $url['port'] : 'PORT=50000';
+  $conn_string['protocol'] = 'PROTOCOL=TCPIP';
+  $conn_string['uid'] = 'UID=' . urldecode($url['user']);
+  $conn_string['pwd'] = 'PWD=' . urldecode($url['pass']);
+
+  // Build db2 connection string and allow for non-standard DB2 port.
+  $conn_string = implode($conn_string, ';');
+
+  // Build db2 connection options array.
+  $conn_options = array(
+    // Turns autocommit on for this connection handle.
+    'autocommit' => DB2_AUTOCOMMIT_ON,
+    // Specifies that column names are returned in lower case.
+    'DB2_ATTR_CASE' => DB2_CASE_LOWER,
+    // Specifies a forward-only cursor for a statement resource.
+    'CURSOR' => DB2_FORWARD_ONLY
+  );
+
+  // Test connecting to the database.
+  $connection = @db2_connect($conn_string, '', '', $conn_options);
+  if (!$connection && ($error = db2_conn_error())) {
+    drupal_maintenance_theme();
+    drupal_set_header('HTTP/1.1 503 Service Unavailable');
+    drupal_set_title('Unable to connect to database');
+    print theme('maintenance_page', '<p>If you still have to install Drupal, proceed to the <a href="'. base_path() .'install.php">installation page</a>.</p>
+<p>If you have already finished installed Drupal, this either means that the username and password information in your <code>settings.php</code> file is incorrect or that we can\'t connect to the DB2 database server. This could mean your hosting provider\'s database server is down.</p>
+<p>The DB2 error was: '. theme('placeholder', decode_entities($php_errormsg)) .'</p>
+<p>Currently, the database is '. theme('placeholder', substr($url['path'], 1)) .', the username is '. theme('placeholder', $url['user']) .', and the database server is '. theme('placeholder', $url['host']) .'.</p>
+<ul>
+  <li>Are you sure you have the correct username and password?</li>
+  <li>Are you sure that you have typed the correct hostname?</li>
+  <li>Are you sure you have the correct database name?</li>
+  <li>Are you sure that the database server is running?</li>
+</ul>
+<p>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.</p>');
+    exit;
+  }
+
+  return $connection;
+}
+
+/**
+ * Regex for rewriting the DB2 reserved words , which are excluded in
+ * ANSI reserved word list.
+ *
+ * NOTE: Here we will assume that all table/column/constrain names are written
+ * in LOWER case, which also means all query element with UPPER case will not
+ * be checked. e.g. 'mode' will replace as '"mode"', but 'MODE' will keep
+ * untouched.
+ *
+ * NOTE: Check http://drupal.org/node/2497 for further information about
+ * Drupal's query coding standard.
+ */
+define('DB2_RESERVED_REGEXP', '/([[:space:],\.\(\)<>\+-=!*]|^)(activate|alias|allow|associate|asutime|attributes|audit|aux|auxiliary|bufferpool|cache|capture|cardinality|ccsid|cluster|collection|collid|comment|concat|count_big|current_lc_ctype|current_schema|current_server|current_timezone|database|datapartitionname|datapartitionnum|days|db2general|db2genrl|db2sql|dbinfo|dbpartitionname|dbpartitionnum|defaults|definition|dense_rank|denserank|disable|disallow|dssize|editproc|enable|encoding|encryption|end-exec|ending|erase|every|excluding|exclusive|explain|fenced|fieldproc|file|final|generated|graphic|hash|hashed_value|hint|hours|including|inclusive|increment|index|inherit|integrity|isobid|jar|java|label|lc_ctype|linktype|localdate|locale|locators|lock|lockmax|locksize|long|maintained|materialized|maxvalue|microsecond|microseconds|minutes|minvalue|mode|months|new_table|nextval|nocache|nocycle|nodename|nodenumber|nomaxvalue|nominvalue|noorder|normalized|nulls|numparts|obid|old_table|optimization|optimize|overriding|package|padded|pagesize|part|partitioned|partitioning|partitions|password|piecesize|plan|prevval|priqty|program|psid|query|queryno|rank|recovery|refresh|rename|reset|restart|result_set_locator|row_number|rownumber|rowset|rrn|run|scratchpad|seconds|secqty|security|sequence|simple|source|sqlid|stacked|standard|starting|statement|stay|stogroup|stores|style|summary|synonym|sysfun|sysibm|sysproc|tablespace|type|validproc|variable|variant|vcat|version|volatile|volumes|wlm|xmlelement|years)([[:space:],\.\(\)<>\+-=!*]|$)/Ds');
+
+/**
+ * Regex for rewriting the DB2's 30 maximum characters limitation on
+ * column naming. Although DB2 support maximum 128 characters for indexes,
+ * savepoints, tables, views and aliases, we will also handle it as column
+ * limitation.
+ *
+ * NOTE: Here we will assume that all table/column/constrain names are written
+ * in LOWER case, which also means all query element with UPPER case will not
+ * be checked. e.g. 'mode' will replace as '"mode"', but 'MODE' will keep
+ * untouched.
+ *
+ * NOTE: Check http://drupal.org/node/2497 for further information about
+ * Drupal's query coding standard.
+ */
+define('DB2_MAX30_REGEXP', '/([[:space:],\.\(\)<>\+-=!*]|^)([a-z0-9_]{31,})([[:space:],\.\(\)<>\+-=!*]|$)/Ds');
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query_callback_db2_max30($match) {
+  static $table_variable = NULL;
+  static $variables = array();
+
+  // Try to fetch mapping, if not exists, set it up.
+  if (!($trim = variable_get('db2_' . $match[2], NULL))) {
+    // Fetch last counter, and +1 for next mapping.
+    $count = variable_get('db2_max30', 0) + 1;
+    $variables['db2_max30'] = $count;
+    // Setup mapping and reverse mapping.
+    $trim = substr($match[2], 0, 30 - strlen($count)) . $count;
+    $variables['db2_' . $match[2]] = $trim;
+    $variables['db2_' . $trim] = $match[2];
+  }
+
+  // NOTE: if the table "variable" is already existed, we will only call
+  // db_table_exists() once, to reduce the total number of queries.
+  if ($table_variable || ($table_variable = db_table_exists('variable'))) {
+    // Always save all cached value, to overcome the conflict when using
+    // variable_set() before creating the table "variable".
+    foreach ($variables as $name => $value) {
+      variable_set($name, $value);
+      unset($variables[$name]);
+    }
+  }
+
+  return $match[1] . $trim . $match[3];
+}
+
+/**
+ * Regex for rewriting the DB2's 18 maximum characters limitation on
+ * constraints naming.
+ *
+ * NOTE: Here we will assume that all table/column/constrain names are written
+ * in LOWER case, which also means all query element with UPPER case will not
+ * be checked. e.g. 'mode' will replace as '"mode"', but 'MODE' will keep
+ * untouched.
+ *
+ * NOTE: Check http://drupal.org/node/2497 for further information about
+ * Drupal's query coding standard.
+ */
+define('DB2_MAX18_REGEXP', '/([[:space:],\.\(\)<>\+-=!*]|^)(ix_|pk_)([a-z0-9_]{16,})([[:space:],\.\(\)<>\+-=!*]|$)/Ds');
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query_callback_db2_max18($match) {
+  static $table_variable = NULL;
+  static $variables = array();
+
+  // Try to fetch mapping, if not exists, set it up.
+  if (!($trim = variable_get('db2_' . $match[2] . $match[3], NULL))) {
+    // Fetch last counter, and +1 for next mapping.
+    $count = variable_get('db2_max18', 0) + 1;
+    $variables['db2_max18'] = $count;
+    // Setup mapping and reverse mapping.
+    $trim = substr($match[2] . $match[3], 0, 18 - strlen($count)) . $count;
+    $variables['db2_' . $match[2] . $match[3]] = $trim;
+    $variables['db2_' . $trim] = $match[2] . $match[3];
+  }
+
+  // NOTE: if the table "variable" is already existed, we will only call
+  // db_table_exists() once, to reduce the total number of queries.
+  if ($table_variable || ($table_variable = db_table_exists('variable'))) {
+    // Always save all cached value, to overcome the conflict when using
+    // variable_set() before creating the table "variable".
+    foreach ($variables as $name => $value) {
+      variable_set($name, $value);
+      unset($variables[$name]);
+    }
+  }
+
+  return $match[1] . $trim . $match[4];
+}
+
+/**
+ * Runs a basic query in the active database.
+ *
+ * User-supplied arguments to the query should be passed in as separate
+ * parameters so that they can be properly escaped to avoid SQL injection
+ * attacks.
+ *
+ * @param $query
+ *   A string containing an SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. Instead of a variable number of query arguments,
+ *   you may also pass a single array containing the query arguments.
+ *
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in ''), %c (character large object, do not enclose in '') and %%.
+ *
+ *   NOTE: use NULL as arguments substitution for %b and %c. This will be
+ *   replaced as corresponding empty LOB value placeholder, based on the
+ *   database specific representation.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @return
+ *   A database query result resource, or FALSE if the query was not
+ *   executed correctly.
+ */
+function db_query($query) {
+  $args = func_get_args();
+  array_shift($args);
+  $query = db_prefix_tables($query);
+  $query = preg_replace(DB2_RESERVED_REGEXP, '\\1"\2"\\3', $query);
+  $query = preg_replace_callback(DB2_MAX18_REGEXP, '_db_query_callback_db2_max18', $query);
+  $query = preg_replace_callback(DB2_MAX30_REGEXP, '_db_query_callback_db2_max30', $query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  return _db_query($query);
+}
+
+$db2_debug = 0;
+/**
+ * Helper function for db_query().
+ */
+function _db_query($query, $debug = 0) {
+  global $active_db, $last_result, $queries, $db2_debug;
+
+  if (variable_get('dev_query', 0)) {
+    list($usec, $sec) = explode(' ', microtime());
+    $timer = (float)$usec + (float)$sec;
+  }
+
+  $last_result = @db2_prepare($active_db, $query);
+  $result = @db2_execute($last_result);
+
+  if (variable_get('dev_query', 0)) {
+    $bt = debug_backtrace();
+    $query = $bt[2]['function'] ."\n". $query;
+    list($usec, $sec) = explode(' ', microtime());
+    $stop = (float)$usec + (float)$sec;
+    $diff = $stop - $timer;
+    $queries[] = array($query, $diff);
+  }
+
+  if ($debug || $db2_debug) {
+    print '<p>query: '. $query .'<br />error:'. db2_stmt_errormsg() .'</p>';
+  }
+
+  if ($last_result !== FALSE) {
+    return $last_result;
+  }
+  else {
+    trigger_error(check_plain(db2_stmt_errormsg() ."\nquery: ". $query), E_USER_WARNING);
+    return FALSE;
+  }
+}
+
+/**
+ * Fetch one result row from the previous query as an object.
+ *
+ * @param $result
+ *   A database query result resource, as returned from db_query().
+ * @return
+ *   An object representing the next row of the result, or FALSE. The attributes
+ *   of this object are the table fields selected by the query.
+ */
+function db_fetch_object($result) {
+  if ($result) {
+    $tmp = @db2_fetch_object($result);
+    if (is_object($tmp)) {
+      $ret = new stdClass();
+      foreach ((array) $tmp as $key => $value) {
+        $column = $key;
+        // Preform short-to-long mapping if required.
+        $column = variable_get('db2_' . $column, $column);
+        $ret->$column = $value;
+      }
+      return $ret;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Fetch one result row from the previous query as an array.
+ *
+ * @param $result
+ *   A database query result resource, as returned from db_query().
+ * @return
+ *   An associative array representing the next row of the result, or FALSE.
+ *   The keys of this object are the names of the table fields selected by the
+ *   query, and the values are the field values for this result row.
+ */
+function db_fetch_array($result) {
+  if ($result) {
+    $tmp = @db2_fetch_assoc($result);
+    if (is_array($tmp)) {
+      $ret = array();
+      foreach ((array) $tmp as $key => $value) {
+        $column = $key;
+        // Preform short-to-long mapping if required.
+        $column = variable_get('db2_' . $column, $column);
+        $ret[$column] = $value;
+      }
+      return $ret;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Return an individual result field from the previous query.
+ *
+ * Only use this function if exactly one field is being selected; otherwise,
+ * use db_fetch_object() or db_fetch_array().
+ *
+ * @param $result
+ *   A database query result resource, as returned from db_query().
+ * @param $row
+ *   The index of the row whose result is needed.
+ * @return
+ *   The resulting field or FALSE.
+ */
+function db_result($result, $row = 0) {
+  if ($result && ($array = @db2_fetch_array($result))) {
+    return $array[0];
+  }
+  return FALSE;
+}
+
+/**
+ * Determine whether the previous query caused an error.
+ */
+function db_error() {
+  global $active_db;
+  return db2_stmt_errormsg($active_db);
+}
+
+/**
+ * Returns the last insert id. This function is thread safe.
+ *
+ * @param $table
+ *   The name of the table you inserted into.
+ * @param $field
+ *   The name of the autoincrement field.
+ */
+function db_last_insert_id($table, $field) {
+  return db_result(db_query("SELECT SYSIBM.IDENTITY_VAL_LOCAL() AS id FROM " . db_prefix_tables('{'. $table .'}')));
+}
+
+/**
+ * Determine the number of rows changed by the preceding query.
+ */
+function db_affected_rows() {
+  global $last_result;
+  if ($last_result) {
+    $count = db2_num_rows($last_result);
+    return $count <= 0 ? 0 : $count;
+  }
+  return 0;
+}
+
+/**
+ * Runs a limited-range query in the active database.
+ *
+ * Use this as a substitute for db_query() when a subset of the query
+ * is to be returned.
+ * User-supplied arguments to the query should be passed in as separate
+ * parameters so that they can be properly escaped to avoid SQL injection
+ * attacks.
+ *
+ * @param $query
+ *   A string containing an SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. Instead of a variable number of query arguments,
+ *   you may also pass a single array containing the query arguments.
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in ''), %c (character large object, do not enclose in '') and %%.
+ *
+ *   NOTE: use NULL as arguments substitution for %b and %c. This will be
+ *   replaced as corresponding empty LOB value placeholder, based on the
+ *   database specific representation.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $from
+ *   The first result row to return.
+ * @param $count
+ *   The maximum number of result rows to return.
+ * @return
+ *   A database query result resource, or FALSE if the query was not executed
+ *   correctly.
+ */
+function db_query_range($query) {
+  $args = func_get_args();
+  $count = array_pop($args);
+  $from = array_pop($args);
+  $last_record = $count + $from;
+  array_shift($args);
+
+  $query = db_prefix_tables($query);
+  $query = preg_replace(DB2_RESERVED_REGEXP, '\\1"\2"\\3', $query);
+  $query = preg_replace_callback(DB2_MAX18_REGEXP, '_db_query_callback_db2_max18', $query);
+  $query = preg_replace_callback(DB2_MAX30_REGEXP, '_db_query_callback_db2_max30', $query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  $query = preg_replace("/SELECT/i", "SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT", $query);
+  $query .= ") AS I) AS O WHERE sys_row_num BETWEEN $from AND $last_record";
+  return _db_query($query);
+}
+
+/**
+ * Runs a SELECT query and stores its results in a temporary table.
+ *
+ * Use this as a substitute for db_query() when the results need to stored
+ * in a temporary table. Temporary tables exist for the duration of the page
+ * request.
+ * User-supplied arguments to the query should be passed in as separate parameters
+ * so that they can be properly escaped to avoid SQL injection attacks.
+ *
+ * Note that if you need to know how many results were returned, you should do
+ * a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() do
+ * not give consistent result across different database types in this case.
+ *
+ * @param $query
+ *   A string containing a normal SELECT SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. The query arguments can be enclosed in one
+ *   array instead.
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in ''), %c (character large object, do not enclose in '') and %%.
+ *
+ *   NOTE: use NULL as arguments substitution for %b and %c. This will be
+ *   replaced as corresponding empty LOB value placeholder, based on the
+ *   database specific representation.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $table
+ *   The name of the temporary table to select into. This name will not be
+ *   prefixed as there is no risk of collision.
+ * @return
+ *   A database query result resource, or FALSE if the query was not executed
+ *   correctly.
+ */
+function db_query_temporary($query) {
+  $args = func_get_args();
+  $tablename = array_pop($args);
+  array_shift($args);
+
+  $query = preg_replace('/^SELECT/i', 'DECLARE GLOBAL TEMPORARY TABLE '. $tablename .' AS (SELECT ', db_prefix_tables($query));
+  $query .= ') DEFINITION ONLY';
+  $query .= ' INCLUDING IDENTITY COLUMN ATTRIBUTES';
+  $query .= ' INCLUDING COLUMN DEFAULTS';
+  $query .= ' ON COMMIT PRESERVE ROWS';
+  $query .= ' NOT LOGGED';
+  $query = preg_replace(DB2_RESERVED_REGEXP, '\\1"\2"\\3', $query);
+  $query = preg_replace_callback(DB2_MAX18_REGEXP, '_db_query_callback_db2_max18', $query);
+  $query = preg_replace_callback(DB2_MAX30_REGEXP, '_db_query_callback_db2_max30', $query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  return _db_query($query);
+}
+
+/**
+ * Returns a properly formatted Binary Large OBject value.
+ *
+ * @param $data
+ *   Data to encode.
+ * @return
+ *  Encoded data, or empty LOB value placeholder for NULL $data.
+ */
+function db_encode_blob($data) {
+  return !is_null($data) ? preg_replace("/'/", "''", $data) : "null";
+}
+
+/**
+ * Returns text from a Binary Large OBject value.
+ *
+ * @param $data
+ *   Data to decode.
+ * @return
+ *  Decoded data.
+ */
+function db_decode_blob($data) {
+  return $data;
+}
+
+/**
+ * Returns a properly formatted Character Large OBject value.
+ *
+ * @param $data
+ *   Data to encode.
+ * @return
+ *  Encoded data, or empty LOB value placeholder for NULL $data.
+ */
+function db_encode_clob($data) {
+  return !is_null($data) ? preg_replace("/'/", "''", $data) : "null";
+}
+
+/**
+ * Returns text from a Character Large OBject value.
+ *
+ * @param $data
+ *   Data to decode.
+ * @return
+ *  Decoded data.
+ */
+function db_decode_clob($data) {
+  return $data;
+}
+
+/**
+ * Prepare user input for use in a database query, preventing SQL injection attacks.
+ */
+function db_escape_string($text) {
+  // Trim first 4000 characters, based on DB2 buffer pool limitation.
+  $text = preg_replace('/(.{0,4000})(.*)/', '\1', $text);
+  // Replace any single ' with ''
+  $text = preg_replace("/'/", "''", $text);
+  return $text;
+}
+
+/**
+ * Update the Binary Large Object value, based on the database specific
+ * implementation.
+ *
+ * @param $query
+ *   A string containing an update condition query (where clause).
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   WHERE condition, using printf() syntax. Instead of a variable number
+ *   of query arguments, you may also pass a single array containing the
+ *   query arguments.
+ *
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in ''), %c (character large object, do not enclose in '') and %%.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $table
+ *   Table to update.
+ * @param $column
+ *   Column to update.
+ * @param $value
+ *   Values to update.
+ * @return
+ *   A database query result resource, or FALSE if the query was not
+ *   executed correctly.
+ */
+function db_update_blob($query) {
+  $args = func_get_args();
+  $value = array_pop($args);
+  $column = array_pop($args);
+  $table = array_pop($args);
+  array_shift($args);
+
+  $query = 'UPDATE ' . $table . ' SET ' . $column . ' = ? WHERE ' . $query;
+  $query = preg_replace(DB2_RESERVED_REGEXP, '\\1"\2"\\3', $query);
+  $query = preg_replace_callback(DB2_MAX18_REGEXP, '_db_query_callback_db2_max18', $query);
+  $query = preg_replace_callback(DB2_MAX30_REGEXP, '_db_query_callback_db2_max30', $query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  return _db_update_lob($query, db_encode_blob($value));
+}
+
+/**
+ * Update the Character Large Object value, based on the database specific
+ * implementation.
+ *
+ * @param $query
+ *   A string containing an update condition query (where clause).
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   WHERE condition, using printf() syntax. Instead of a variable number
+ *   of query arguments, you may also pass a single array containing the
+ *   query arguments.
+ *
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in ''), %c (character large object, do not enclose in '') and %%.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $table
+ *   Table to update.
+ * @param $column
+ *   Column to update.
+ * @param $value
+ *   Values to update.
+ * @return
+ *   A database query result resource, or FALSE if the query was not
+ *   executed correctly.
+ */
+function db_update_clob($query) {
+  $args = func_get_args();
+  $value = array_pop($args);
+  $column = array_pop($args);
+  $table = array_pop($args);
+  array_shift($args);
+
+  $query = 'UPDATE ' . $table . ' SET ' . $column . ' = ? WHERE ' . $query;
+  $query = preg_replace(DB2_RESERVED_REGEXP, '\\1"\2"\\3', $query);
+  $query = preg_replace_callback(DB2_MAX18_REGEXP, '_db_query_callback_db2_max18', $query);
+  $query = preg_replace_callback(DB2_MAX30_REGEXP, '_db_query_callback_db2_max30', $query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  return _db_update_lob($query, db_encode_clob($value));
+}
+
+/**
+ * Helper function for update Large Object value.
+ *
+ * Reference: "Working with LOBs in Oracle and PHP":
+ * http://hk.php.net/manual/en/function.db2-prepare.php
+ */
+function _db_update_lob($query, $value) {
+  global $active_db, $last_result, $queries;
+
+  if (variable_get('dev_query', 0)) {
+    list($usec, $sec) = explode(' ', microtime());
+    $timer = (float)$usec + (float)$sec;
+  }
+
+  $last_result = @db2_prepare($active_db, $query);
+  $result = @db2_execute($last_result, array($value));
+
+  if (variable_get('dev_query', 0)) {
+    $bt = debug_backtrace();
+    $query = $bt[2]['function'] . "\n" . $query;
+    list($usec, $sec) = explode(' ', microtime());
+    $stop = (float)$usec + (float)$sec;
+    $diff = $stop - $timer;
+    $queries[] = array($query, $diff);
+  }
+
+  if ($last_result !== FALSE) {
+    return TRUE;
+  }
+  else {
+    trigger_error(check_plain(db2_stmt_errormsg() ."\nquery: ". $query), E_USER_WARNING);
+    return FALSE;
+  }
+}
+
+/**
+ * Lock a table.
+ * This function automatically starts a transaction.
+ */
+function db_lock_table($table) {
+  db_query('LOCK TABLE {'. db_escape_table($table) .'} IN EXCLUSIVE MODE');
+}
+
+/**
+ * Unlock all locked tables.
+ * This function automatically commits a transaction.
+ */
+function db_unlock_tables() {
+  global $active_db;
+  db2_commit($active_db);
+}
+
+/**
+ * Check if a table exists.
+ */
+function db_table_exists($table) {
+  return db_result(db_query("SELECT COUNT(table_name) FROM sysibm.tables WHERE table_name = '{". db_escape_table($table) ."}'"));
+}
+
+/**
+ * Check if a column exists in the given table.
+ */
+function db_column_exists($table, $column) {
+  return db_result(db_query("SELECT COUNT(column) FROM sysibm.columns WHERE table_name = '{". db_escape_table($table) ."}' AND column_name = " . $column));
+}
+
+/**
+ * Wraps the given table.field entry with a DISTINCT(). The wrapper is added to
+ * the SELECT list entry of the given query and the resulting query is returned.
+ * This function only applies the wrapper if a DISTINCT doesn't already exist in
+ * the query.
+ *
+ * @param $table Table containing the field to set as DISTINCT
+ * @param $field Field to set as DISTINCT
+ * @param $query Query to apply the wrapper to
+ * @return SQL query with the DISTINCT wrapper surrounding the given table.field.
+ */
+function db_distinct_field($table, $field, $query) {
+  $field_to_select = 'DISTINCT ON ('. $table .'.'. $field .") $table.$field";
+  // (?<!text) is a negative look-behind (no need to rewrite queries that already use DISTINCT).
+  $query = preg_replace('/(SELECT.*)(?:'. $table .'\.|\s)(?<!DISTINCT\()(?<!DISTINCT\('. $table .'\.)'. $field .'(.*FROM )/AUsi', '\1 '. $field_to_select .'\2', $query);
+  $query = preg_replace('/(ORDER BY )(?!'. $table .'\.'. $field .')/', '\1'."$table.$field, ", $query);
+  return $query;
+}
+
+/**
+ * @} End of "ingroup database".
+ */
+
+/**
+ * @ingroup schemaapi
+ * @{
+ */
+
+/**
+ * This maps a generic data type in combination with its data size
+ * to the engine-specific data type.
+ */
+function db_type_map() {
+  // 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.
+  $map = array(
+    'varchar:normal' => 'VARCHAR',
+
+    'text:tiny' => 'VARCHAR(4000)',
+    'text:small' => 'VARCHAR(4000)',
+    'text:medium' => 'VARCHAR(4000)',
+    'text:big' => 'VARCHAR(4000)',
+    'text:normal' => 'VARCHAR(4000)',
+
+    'int:tiny' => 'SMALLINT',
+    'int:small' => 'SMALLINT',
+    'int:medium' => 'INTEGER',
+    'int:big' => 'BIGINT',
+    'int:normal' => 'INTEGER',
+
+    'float:tiny' => 'REAL',
+    'float:small' => 'REAL',
+    'float:medium' => 'REAL',
+    'float:big' => 'DOUBLE',
+    'float:normal' => 'REAL',
+
+    'numeric:normal'  => 'NUMERIC',
+
+    'blob:big' => 'BLOB(2G)',
+    'blob:normal' => 'BLOB(2G)',
+
+    'clob:big' => 'CLOB(2G)',
+    'clob:normal' => 'CLOB(2G)',
+
+    'datetime:normal' => 'TIMESTAMP',
+
+    'serial:tiny' => 'INTEGER',
+    'serial:small' => 'INTEGER',
+    'serial:medium' => 'INTEGER',
+    'serial:big' => 'BIGINT',
+    'serial:normal' => 'INTEGER',
+  );
+  return $map;
+}
+
+/**
+ * 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.
+ */
+function db_create_table_sql($name, $table) {
+  $sql_keys = array();
+  $_index = array();
+  if (isset($table['primary key']) && is_array($table['primary key'])) {
+    $sql_keys[] = 'CONSTRAINT pk_{' . $name . '} PRIMARY KEY ('. implode(', ', $table['primary key']) .')';
+    // Record primary key column.
+    $_key = implode('_', $table['primary key']);
+    $_index[$_key] = 1;
+  }
+  if (isset($table['unique keys']) && is_array($table['unique keys'])) {
+    foreach ($table['unique keys'] as $key_name => $key) {
+      $sql_keys[] = 'CONSTRAINT ix_{'. $name .'}_'. $key_name .' UNIQUE ('. implode(', ', $key) .')';
+    }
+  }
+
+  $sql_fields = array();
+  foreach ($table['fields'] as $field_name => $field) {
+    $sql_fields[] = _db_create_field_sql($field_name, _db_process_field($field));
+  }
+
+  $sql = 'CREATE TABLE {'. $name .'} (' . "\n\t";
+  $sql .= implode(",\n\t", $sql_fields);
+  if (count($sql_keys) > 0) {
+    $sql .= ",\n\t";
+  }
+  $sql .= implode(",\n\t", $sql_keys);
+  $sql .= "\n)";
+  $statements[] = $sql;
+
+  if (isset($table['indexes']) && is_array($table['indexes'])) {
+    foreach ($table['indexes'] as $key_name => $key) {
+      $_key = str_replace(', ', '_', _db_create_key_sql($key));
+      if (!isset($_index[$_key]) || !$_index[$_key]) {
+        // Don't create index for primary key, due to Oracle limitation.
+        $statements[] = _db_create_index_sql($name, $key_name, $key);
+      }
+    }
+  }
+
+  return $statements;
+}
+
+function _db_create_index_sql($table, $name, $fields) {
+  $query = 'CREATE INDEX ix_{'. $table .'}_'. $name .' ON {'. $table .'} (';
+  $query .= _db_create_key_sql($fields) .')';
+  return $query;
+}
+
+function _db_create_key_sql($fields) {
+  $ret = array();
+  foreach ($fields as $field) {
+    $ret[] = is_array($field) ? $field[0] : $field;
+  }
+  return implode(', ', $ret);
+}
+
+/**
+ * Set database-engine specific properties for a field.
+ *
+ * @param $field
+ *   A field description array, as specified in the schema documentation.
+ */
+function _db_process_field($field) {
+  if (!isset($field['size'])) {
+    $field['size'] = 'normal';
+  }
+  // Set the correct database-engine specific datatype.
+  if (!isset($field['db2_type'])) {
+    $map = db_type_map();
+    $field['db2_type'] = $map[$field['type'] .':'. $field['size']];
+  }
+  if ($field['type'] == 'serial') {
+    unset($field['not null']);
+  }
+  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_process_field().
+ *
+ * @param $name
+ *    Name of the field.
+ * @param $spec
+ *    The field specification, as per the schema data structure format.
+ */
+function _db_create_field_sql($name, $spec) {
+  $sql = $name .' '. $spec['db2_type'];
+
+  if (($spec['type'] == 'blob') || ($spec['type'] == 'clob')) {
+    $sql .= " NOT LOGGED NOT COMPACT";
+    unset($spec['not null']);
+  }
+
+  if (!empty($spec['length'])) {
+    $sql .= '('. $spec['length'] .')';
+  }
+  elseif (isset($spec['precision']) && isset($spec['scale'])) {
+    $sql .= '('. $spec['scale'] .', '. $spec['precision'] .')';
+  }
+
+  if (isset($spec['not null']) && $spec['not null']) {
+    $sql .= ' NOT NULL';
+  }
+
+  if (isset($spec['default'])) {
+    $default = is_string($spec['default']) ? "'". $spec['default'] ."'" : $spec['default'];
+    $sql .= " DEFAULT $default";
+  }
+
+  if (!empty($spec['unsigned'])) {
+    $sql .= ' CHECK (' . $name . ' >= 0)';
+  }
+
+  if ($spec['type'] == 'serial') {
+    $sql .= ' GENERATED ALWAYS AS IDENTITY (START WITH +1 INCREMENT BY +1 NO CYCLE NO CACHE ORDER)';
+  }
+
+  return $sql;
+}
+
+/**
+ * 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.
+ */
+function db_rename_table(&$ret, $table, $new_name) {
+  $ret[] = update_sql('RENAME TABLE {'. $table .'} TO {'. $new_name .'}');
+}
+
+/**
+ * Drop a table.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be dropped.
+ */
+function db_drop_table(&$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
+ */
+function db_add_field(&$ret, $table, $field, $spec) {
+  $query = 'ALTER TABLE {'. $table .'} ADD ';
+  $query .= _db_create_field_sql($field, _db_process_field($spec));
+  $ret[] = update_sql($query);
+}
+
+/**
+ * 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.
+ */
+function db_drop_field(&$ret, $table, $field) {
+  $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP '. $field);
+}
+
+/**
+ * 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'.
+ */
+function db_field_set_default(&$ret, $table, $field, $default) {
+  if ($default == NULL) {
+    $default = 'NULL';
+  }
+  else {
+    $default = is_string($default) ? "'$default'" : $default;
+  }
+
+  $ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER '. $field .' SET DEFAULT '. $default);
+}
+
+/**
+ * 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.
+ */
+function db_field_set_no_default(&$ret, $table, $field) {
+  $ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER '. $field .' DROP DEFAULT');
+}
+
+/**
+ * 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.
+ */
+function db_add_primary_key(&$ret, $table, $fields) {
+  $ret[] = update_sql('ALTER TABLE {'. $table .'} ADD CONSTRAINT pk_{' . $table . '} PRIMARY KEY (' . implode(',', $fields) . ')');
+}
+
+/**
+ * Drop the primary key.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ */
+function db_drop_primary_key(&$ret, $table) {
+  $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP PRIMARY KEY');
+}
+
+/**
+ * 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.
+ */
+function db_add_unique_key(&$ret, $table, $name, $fields) {
+  $name = 'ix_{'. $table .'}_'. $name;
+  $ret[] = update_sql('ALTER TABLE {' . $table . '} ADD CONSTRAINT ' . $name . ' UNIQUE (' . implode(',', $fields) . ')');
+}
+
+/**
+ * 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.
+ */
+function db_drop_unique_key(&$ret, $table, $name) {
+  $name = 'ix_{'. $table .'}_'. $name;
+  $ret[] = update_sql('ALTER TABLE {'. $table .'} DROP UNIQUE '. $name);
+}
+
+/**
+ * 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.
+ */
+function db_add_index(&$ret, $table, $name, $fields) {
+  $ret[] = update_sql(_db_create_index_sql($table, $name, $fields));
+}
+
+/**
+ * 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.
+ */
+function db_drop_index(&$ret, $table, $name) {
+  $name = 'ix_{'. $table .'}_'. $name;
+  $ret[] = update_sql('DROP INDEX '. $name);
+}
+
+/**
+ * Change a field definition.
+ *
+ * Remember that changing a field definition involves adding a new field
+ * and dropping an old one. This means that any indices, primary keys and
+ * sequences from serial-type fields are dropped and might need to be
+ * recreated.
+ *
+ * @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.
+ */
+function db_change_field(&$ret, $table, $field, $field_new, $spec) {
+  // NOTE: DB2 don't support column rename query (known issue), so first of
+  // all we need to copy old column data to temp column, drop old column, and
+  // copy it back to new column.
+  db_add_field($ret, $table, "new_" . $field_new, $spec);
+  $ret[] = update_sql('UPDATE {'. $table .'} SET new_' . $field_new . " = ". $field);
+  db_drop_field($ret, $table, $field);
+  db_add_field($ret, $table, "$field_new", $spec);
+  $ret[] = update_sql('UPDATE {'. $table .'} SET ' . $field_new . " = new_". $field);
+  $not_null = isset($spec['not null']) ? $spec['not null'] : FALSE;
+  unset($spec['not null']);
+  if ($not_null) {
+    $ret[] = update_sql('ALTER TABLE {'. $table .'} ALTER ' . $field_new . "SET NOT NULL");
+  }
+  db_drop_field($ret, $table, "new_" . $field);
+}
+
+/**
+ * Update a field definition to match its schema. If the field is
+ * involved in any keys or indexes, recreate them if necessary.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   Name of the table.
+ * @param $field
+ *   Name of the field to update.
+ */
+function db_update_field(&$ret, $table, $field) {
+  $spec = drupal_get_schema($table);
+
+  db_change_field($ret, $table, $field, $field, $spec['fields'][$field]);
+  if (isset($spec['primary key'])) {
+    if (array_search($field, db_field_names($spec['primary key'])) !== FALSE) {
+      db_add_primary_key($ret, $table, $spec['primary key']);
+    }
+  }
+  if (isset($spec['unique keys'])) {
+    foreach ($spec['unique keys'] as $name => $fields) {
+      if (array_search($field, db_field_names($fields)) !== FALSE) {
+        db_add_unique_key($ret, $table, $fields);
+      }
+    }
+  }
+  if (isset($spec['indexes'])) {
+    foreach ($spec['indexes'] as $name => $fields) {
+      if (array_search($field, db_field_names($fields)) !== FALSE) {
+        db_add_index($ret, $table, $fields);
+      }
+    }
+  }
+}
+
+/**
+ * @} End of "ingroup schemaapi".
+ */
diff -urpN prepatch-dev/includes/install.db2.inc drupal-6.x-dev-db2-0.4/includes/install.db2.inc
--- prepatch-dev/includes/install.db2.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-db2-0.4/includes/install.db2.inc	2007-08-09 10:36:52.000000000 +0800
@@ -0,0 +1,152 @@
+<?php
+// $Id$
+
+// DB2 specific install functions
+
+/**
+ * Check if DB2 is available.
+ *
+ * @return
+ *  TRUE/FALSE
+ */
+function db2_is_available() {
+  return function_exists('db2_connect');
+}
+
+/**
+ * Check if we can connect to DB2.
+ *
+ * @return
+ *  TRUE/FALSE
+ */
+function drupal_test_db2($url, &$success) {
+  if (!db2_is_available()) {
+    drupal_set_message(st('PHP DB2 support not enabled.'), 'error');
+    return FALSE;
+  }
+
+  $url = parse_url($url);
+
+  // Decode url-encoded information in the db connection string.
+  $conn_string['database'] = 'DATABASE=' . substr(urldecode($url['path']), 1);
+  $conn_string['hostname'] = 'HOSTNAME=' . urldecode($url['host']);
+  $conn_string['port'] = isset($url['port']) ? 'PORT=' . $url['port'] : 'PORT=50000';
+  $conn_string['protocol'] = 'PROTOCOL=TCPIP';
+  $conn_string['uid'] = 'UID=' . urldecode($url['user']);
+  $conn_string['pwd'] = 'PWD=' . urldecode($url['pass']);
+
+  // Build db2 connection string and allow for non-standard DB2 port.
+  $conn_string = implode($conn_string, ';');
+
+  // Build db2 connection options array.
+  $conn_options = array(
+    // Turns autocommit on for this connection handle.
+    'autocommit' => DB2_AUTOCOMMIT_ON,
+    // Specifies that column names are returned in lower case.
+    'DB2_ATTR_CASE' => DB2_CASE_LOWER,
+    // Specifies a forward-only cursor for a statement resource.
+    'CURSOR' => DB2_FORWARD_ONLY
+  );
+
+  // Test connecting to the database.
+  $connection = @db2_connect($conn_string, '', '', $conn_options);
+  if (!$connection && $error = db2_conn_errormsg()) {
+    drupal_set_message(st('Failure to connect to your DB2 database server. DB2 reports the following message: %error.<ul><li>Are you sure you have the correct username and password?</li><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li><li>Are you sure you typed the correct database name?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%error' => $error)), 'error');
+    return FALSE;
+  }
+
+  $success = array('CONNECT');
+
+  // Test CREATE.
+  $query = 'CREATE TABLE drupal_install_test (id integer NOT NULL)';
+  $stmt = db2_prepare($connection, $query);
+  if (!$stmt && $error = db2_stmt_errormsg($stmt)) {
+    drupal_set_message(st('We were unable to create a test table on your DB2 database server with the command %query. DB2 reports the following message: %error.<ul><li>Are you sure the configured username has the necessary DB2 permissions to create tables in the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%query' => $query, '%error' => $error)), 'error');
+    return FALSE;
+  }
+  $result = @db2_execute($stmt);
+  $err = FALSE;
+  $success[] = 'SELECT';
+  $success[] = 'CREATE';
+
+  // Test INSERT.
+  $query = 'INSERT INTO drupal_install_test (id) VALUES (1)';
+  $stmt = db2_prepare($connection, $query);
+  $result = @db2_execute($stmt);
+  if (!$result && $error = db2_stmt_errormsg($stmt)) {
+    drupal_set_message(st('We were unable to insert a value into a test table on your DB2 database server. We tried inserting a value with the command %query and DB2 reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'INSERT';
+  }
+
+  // Test UPDATE.
+  $query = 'UPDATE drupal_install_test SET id = 2';
+  $stmt = db2_prepare($connection, $query);
+  $result = @db2_execute($stmt);
+  if (!$result && $error = db2_stmt_errormsg($stmt)) {
+    drupal_set_message(st('We were unable to update a value in a test table on your DB2 database server. We tried updating a value with the command %query and DB2 reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'UPDATE';
+  }
+
+  // Test LOCK.
+  $query = 'LOCK TABLE drupal_install_test IN EXCLUSIVE MODE';
+  $stmt = db2_prepare($connection, $query);
+  $result = @db2_execute($stmt);
+  if (!$result && $error = db2_stmt_errormsg($stmt)) {
+    drupal_set_message(st('We were unable to lock a test table on your DB2 database server. We tried locking a table with the command %query and DB2 reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'LOCK';
+  }
+
+  // Test UNLOCK, which is done automatically upon transaction end in DB2
+  $query = 'COMMIT';
+  $stmt = db2_prepare($connection, $query);
+  $result = @db2_execute($stmt);
+  if (!$result && $error = db2_stmt_errormsg($stmt)) {
+    drupal_set_message(st('We were unable to unlock a test table on your DB2 database server. We tried unlocking a table with the command %query and DB2 reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'UNLOCK';
+  }
+
+  // Test DELETE.
+  $query = 'DELETE FROM drupal_install_test';
+  $stmt = db2_prepare($connection, $query);
+  $result = @db2_execute($stmt);
+  if (!$result && $error = db2_stmt_errormsg($stmt)) {
+    drupal_set_message(st('We were unable to delete a value from a test table on your DB2 database server. We tried deleting a value with the command %query and DB2 reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'DELETE';
+  }
+
+  // Test DROP.
+  $query = 'DROP TABLE drupal_install_test';
+  $stmt = db2_prepare($connection, $query);
+  $result = @db2_execute($stmt);
+  if (!$result && $error = db2_stmt_errormsg($stmt)) {
+    drupal_set_message(st('We were unable to drop a test table from your DB2 database server. We tried dropping a table with the command %query and DB2 reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'DROP';
+  }
+
+  if ($err) {
+    return FALSE;
+  }
+
+  db2_close($connection);
+  return TRUE;
+}
+
+?>
diff -urpN prepatch-dev/includes/install.inc drupal-6.x-dev-db2-0.4/includes/install.inc
--- prepatch-dev/includes/install.inc	2007-07-31 03:22:47.000000000 +0800
+++ drupal-6.x-dev-db2-0.4/includes/install.inc	2007-08-09 10:36:52.000000000 +0800
@@ -144,7 +144,7 @@ function drupal_detect_baseurl($file = '
 function drupal_detect_database_types() {
   $databases = array();
 
-  foreach (array('mysql', 'mysqli', 'pgsql') as $type) {
+  foreach (array('mysql', 'mysqli', 'pgsql', 'db2') as $type) {
     if (file_exists('./includes/install.'. $type .'.inc')) {
       include_once './includes/install.'. $type .'.inc';
       $function = $type .'_is_available';
