diff -urpN drupal-6.x-dev-200708141658/includes/bootstrap.inc drupal-6.x-dev-mssql-0.1/includes/bootstrap.inc
--- drupal-6.x-dev-200708141658/includes/bootstrap.inc	2007-08-07 16:41:24.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/bootstrap.inc	2007-08-14 17:53:06.000000000 +0800
@@ -331,6 +331,10 @@ function conf_init() {
     ini_set('session.cookie_domain', $cookie_domain);
   }
   session_name('SESS'. md5($session_name));
+
+  if (!function_exists('variable_get')) {
+    require_once './includes/variable.inc';
+  }
 }
 
 /**
@@ -430,61 +434,6 @@ function variable_init($conf = array()) 
 }
 
 /**
- * Return a persistent variable.
- *
- * @param $name
- *   The name of the variable to return.
- * @param $default
- *   The default value to use if this variable has never been set.
- * @return
- *   The value of the variable.
- */
-function variable_get($name, $default) {
-  global $conf;
-
-  return isset($conf[$name]) ? $conf[$name] : $default;
-}
-
-/**
- * Set a persistent variable.
- *
- * @param $name
- *   The name of the variable to set.
- * @param $value
- *   The value to set. This can be any PHP data type; these functions take care
- *   of serialization as necessary.
- */
-function variable_set($name, $value) {
-  global $conf;
-
-  $serialized_value = serialize($value);
-  db_query("UPDATE {variable} SET value = '%s' WHERE name = '%s'", $serialized_value, $name);
-  if (!db_affected_rows()) {
-    @db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, $serialized_value);
-  }
-
-  cache_clear_all('variables', 'cache');
-
-  $conf[$name] = $value;
-}
-
-/**
- * Unset a persistent variable.
- *
- * @param $name
- *   The name of the variable to undefine.
- */
-function variable_del($name) {
-  global $conf;
-
-  db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
-  cache_clear_all('variables', 'cache');
-
-  unset($conf[$name]);
-}
-
-
-/**
  * Retrieve the current page from the cache.
  *
  * Note: we do not serve cached pages when status messages are waiting (from
diff -urpN drupal-6.x-dev-200708141658/includes/cache.inc drupal-6.x-dev-mssql-0.1/includes/cache.inc
--- drupal-6.x-dev-200708141658/includes/cache.inc	2007-05-26 05:01:30.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/cache.inc	2007-08-14 17:52:58.000000000 +0800
@@ -105,10 +105,11 @@ function cache_set($cid, $data, $table =
     $serialized = 1;
   }
   $created = time();
-  db_query("UPDATE {". $table ."} SET data = %b, created = %d, expire = %d, headers = '%s', serialized = %d WHERE cid = '%s'", $data, $created, $expire, $headers, $serialized, $cid);
+  db_query("UPDATE {". $table ."} SET data = %b, created = %d, expire = %d, headers = '%s', serialized = %d WHERE cid = '%s'", NULL, $created, $expire, $headers, $serialized, $cid);
   if (!db_affected_rows()) {
-    @db_query("INSERT INTO {". $table ."} (cid, data, created, expire, headers, serialized) VALUES ('%s', %b, %d, %d, '%s', %d)", $cid, $data, $created, $expire, $headers, $serialized);
+    @db_query("INSERT INTO {". $table ."} (cid, data, created, expire, headers, serialized) VALUES ('%s', %b, %d, %d, '%s', %d)", $cid, NULL, $created, $expire, $headers, $serialized);
   }
+  db_update_blob("cid = '%s'", $cid, db_prefix_tables('{' . $table . '}'), 'data', $data);
 }
 
 /**
diff -urpN drupal-6.x-dev-200708141658/includes/database.inc drupal-6.x-dev-mssql-0.1/includes/database.inc
--- drupal-6.x-dev-200708141658/includes/database.inc	2007-08-06 18:25:56.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/database.inc	2007-08-14 17:52:58.000000000 +0800
@@ -175,15 +175,17 @@ function _db_query_callback($match, $ini
       return '%';
     case '%f':
       return (float) array_shift($args);
-    case '%b': // binary data
-      return db_encode_blob(array_shift($args));
+    case '%b': // Binary Large OBject.
+      return db_encode_blob(array_shift($args));  
+    case '%c': // Character Large OBject.
+      return db_encode_clob(array_shift($args));
   }
 }
 
 /**
  * Indicates the place holders that should be replaced in _db_query_callback().
  */
-define('DB_QUERY_REGEXP', '/(%d|%s|%%|%f|%b)/');
+define('DB_QUERY_REGEXP', '/(%d|%s|%%|%f|%b|%c)/');
 
 /**
  * Helper function for db_rewrite_sql.
diff -urpN drupal-6.x-dev-200708141658/includes/database.mssql.inc drupal-6.x-dev-mssql-0.1/includes/database.mssql.inc
--- drupal-6.x-dev-200708141658/includes/database.mssql.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/database.mssql.inc	2007-08-15 23:55:09.000000000 +0800
@@ -0,0 +1,1108 @@
+<?php
+// $Id$
+
+function _print($msg) {
+  print("<code><pre>");
+  print_r($msg);
+  print("</pre></code>");
+}
+
+/**
+ * @file
+ * Database interface code for MS MSQL database servers.
+ */
+
+/**
+ * @ingroup database
+ * @{
+ */
+
+/**
+ * Report database status.
+ */
+function db_status_report() {
+  $t = get_t();
+
+  $version = db_version();
+
+  $form['mssql'] = array(
+    'title' => $t('MS MSQL 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 SERVERPROPERTY('productversion')"));
+}
+
+/**
+ * Initialize a database connection.
+ */
+function db_connect($url) {
+  // Check if MySQL support is present in PHP
+  if (!function_exists('mssql_connect')) {
+    drupal_maintenance_theme();
+    drupal_set_title('PHP MS MSQL support not enabled');
+    print theme('maintenance_page', '<p>We were unable to use the MS MSQL database because the MS MSQL 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.
+  $url['user'] = urldecode($url['user']);
+  $url['pass'] = urldecode($url['pass']);
+  $url['host'] = urldecode($url['host']);
+  $url['path'] = urldecode($url['path']);
+
+  // Build mssql connection string and allow for non-standard MS SQL port.
+  $url['host'] = isset($url['port']) ? $url['host'] . ',' . $url['port'] : $url['host'];
+
+  // Test connecting to the database.
+  $connection = @mssql_connect($url['host'], $url['user'], $url['pass']);
+  if (!$connection || !mssql_select_db(substr($url['path'], 1))) {
+    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 MS MSQL database server. This could mean your hosting provider\'s database server is down.</p>
+<p>The MS MSQL error was: '. theme('placeholder', mssql_get_last_message()) .'</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 ANSI SQL-92/99/2003 reserved words , which are
+ * still using within drupal core schema.
+ *
+ * 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('ANSI_RESERVED_REGEXP', '/([[:space:],\.\(\)<>\+-=!*]|^)(data|depth|external|language|module|path|position|session|timestamp|translate|value)([[:space:],\.\(\)<>\+-=!*]|$)/Ds');
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query_callback_ansi_reserved($match) {
+  return $match[1] . '[' . $match[2] . ']' . $match[3];
+}
+
+/**
+ * Regex for rewriting the MS SQL 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('MSSQL_RESERVED_REGEXP', '/([[:space:],\.\(\)<>\+-=!*]|^)(backup|break|browse|bulk|checkpoint|clustered|compute|containstable|database|dbcc|deny|disk|distributed|dump|errlvl|file|fillfactor|freetext|freetexttable|holdlock|identitycol|identity_insert|indexin|innerindex|insertinner|intersectinsert|intointersect|isinto|joinis|keyjoin|killkey|leftkill|likeleft|linenolike|loadlineno|national load|nochecknational|nonclusterednocheck|notnonclustered|nullifnull|nullnot|offof|offsetsoff|ofnullif|onoffsets|opendatasourceopen|openon|openqueryopendatasource|openrowsetopenquery|openxmlopenrowset|optionopenxml|orderor|oroption|outerorder|overouter|percentover|pivotpercent|planpivot|plan precision|print|proc|raiserror|readtext|reconfigure|replication|restore|revert|rowcount|rowguidcol|rule|save|securityaudit|setuser|shutdown|statistics|textsize|top|tran|truncate|tsequal|unpivot|updatetext|use|waitfor|writetext)([[:space:],\.\(\)<>\+-=!*]|$)/Ds');
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query_callback_mssql_reserved($match) {
+  return $match[1] . '[' . $match[2] . ']' . $match[3];
+}
+
+/**
+ * Regex for rewriting the MS SQL's 30 maximum characters limitation on
+ * constrain 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('MSSQL_MAX30_REGEXP', '/([[:space:],\.\(\)<>\+-=!*]|^)([a-z0-9_]{31,})([[:space:],\.\(\)<>\+-=!*]|$)/Ds');
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query_callback_mssql_max30($match) {
+  // Try to fetch mapping, if not exists, set it up.
+  if (!($trim = variable_get('mssql_' . $match[2], NULL))) {
+    // Fetch last counter, and +1 for next mapping.
+    $count = variable_get('mssqlmax30', 0) + 1;
+    variable_set('mssql_max30', $count);
+    // Setup mapping and reverse mapping.
+    $trim = substr($match[2], 0, 30 - strlen($count)) . $count;
+    variable_set('mssql_' . $match[2], $trim);
+    variable_set('mssql_' . $trim, $match[2]);
+  }
+  return $match[1] . $trim . $match[3];
+}
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query_rewrite_temp_tablename($query) {
+  global $temp_tablenames;
+  if (count($temp_tablenames)) {
+    return preg_replace('/([[:space:],\.\(\)<>\+-=!*]|^)(' . implode('|', $temp_tablenames) . ')([[:space:],\.\(\)<>\+-=!*]|$)/Ds', '/1#/2/3', $query);
+  }
+  return $query;
+}
+
+
+/**
+ * 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 = _db_query_rewrite_temp_tablename($query);
+  $query = preg_replace_callback(ANSI_RESERVED_REGEXP, '_db_query_callback_ansi_reserved', $query);
+  $query = preg_replace_callback(MSSQL_RESERVED_REGEXP, '_db_query_callback_mssql_reserved', $query);
+  $query = preg_replace_callback(MSSQL_MAX30_REGEXP, '_db_query_callback_mssql_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);
+}
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query($query, $debug = 0) {
+  global $active_db, $last_result, $queries;
+
+  if (variable_get('dev_query', 0)) {
+    list($usec, $sec) = explode(' ', microtime());
+    $timer = (float)$usec + (float)$sec;
+  }
+
+  $last_result = @mssql_query($query, $active_db);
+  _print(check_plain(mssql_get_last_message() ."\nquery: ". $query));
+
+  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) {
+    print '<p>query: '. $query .'<br />error:'. mssql_get_last_message() .'</p>';
+  }
+
+  if ($last_result !== FALSE) {
+    return $last_result;
+  }
+  else {
+    trigger_error(check_plain(mssql_get_last_message() ."\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 = @mssql_fetch_object($result);
+    if (is_object($tmp)) {
+      $ret = new stdClass();
+      foreach ((array) $tmp as $key => $value) {
+        if (!($column = variable_get('mssql_' . $key, NULL))) {
+          $column = $key;
+          // Preform short-to-long mapping if required.
+          $column = variable_get('mssql_' . $column, $column);
+          variable_set('mssql_' . $key, $column);
+        }
+        // http://bugs.php.net/bug.php?id=26996
+        $ret->$column = trim($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 = @mssql_fetch_assoc($result);
+    if (is_array($tmp)) {
+      $ret = array();
+      foreach ((array) $tmp as $key => $value) {
+        if (!($column = variable_get('mssql_' . $key, NULL))) {
+          $column = $key;
+          // Preform short-to-long mapping if required.
+          $column = variable_get('mssql_' . $column, $column);
+          variable_set('mssql_' . $key, $column);
+        }
+        // http://bugs.php.net/bug.php?id=26996
+        $ret[$column] = trim($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().
+ * @return
+ *   The resulting field or FALSE.
+ */
+function db_result($result) {
+  if ($result && mssql_num_rows($result) > 0) {
+    $array = mssql_fetch_row($result);
+    return $array[0];
+  }
+  return FALSE;
+}
+
+/**
+ * Determine whether the previous query caused an error.
+ */
+function db_error() {
+  return mssql_get_last_message();
+}
+
+/**
+ * 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) {
+  // @@IDENTITY will return the last identity value entered into a table in
+  // your current session. While @@IDENTITY is limited to the current
+  // session, it is not limited to the current scope. 
+  return db_result(db_query("SELECT @@IDENTITY"));
+}
+
+/**
+ * Determine the number of rows changed by the preceding query.
+ */
+function db_affected_rows() {
+  return db_result(db_query("SELECT @@ROWCOUNT"));
+}
+
+/**
+ * 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);
+  array_shift($args);
+
+  $query = db_prefix_tables($query);
+  $query = _db_query_rewrite_temp_tablename($query);
+  $query = preg_replace_callback(ANSI_RESERVED_REGEXP, '_db_query_callback_ansi_reserved', $query);
+  $query = preg_replace_callback(MSSQL_RESERVED_REGEXP, '_db_query_callback_mssql_reserved', $query);
+  $query = preg_replace_callback(MSSQL_MAX30_REGEXP, '_db_query_callback_mssql_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 1 AS dummy, ', $query);
+  $query = 'SELECT * FROM (SELECT sub.*, ROW_NUMBER() OVER(ORDER BY sub.dummy) AS line FROM (' . $query . ') AS sub) AS wrapper WHERE line BETWEEN ' . ($from + 1) . ' AND ' . ($from + $count);
+  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() does
+ * 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);
+  $temp_tables[] = $tablename;
+  array_shift($args);
+
+  $query = preg_replace('/^(SELECT.*)(FROM.*)$/i', '\1 INTO #' . $tablename . ' \2', $query);
+  $query = _db_query_rewrite_temp_tablename($query);
+  $query = preg_replace_callback(ANSI_RESERVED_REGEXP, '_db_query_callback_ansi_reserved', $query);
+  $query = preg_replace_callback(MSSQL_RESERVED_REGEXP, '_db_query_callback_mssql_reserved', $query);
+  $query = preg_replace_callback(MSSQL_MAX30_REGEXP, '_db_query_callback_mssql_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.
+ * In case of MS MSQL encodes data for insert or update into bytea field.
+ *
+ * @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) ? bin2hex($data) : "NULL";
+}
+
+/**
+ * Returns text from a Binary Large OBject value.
+ * In case of MS MSQL decodes data after select from bytea field.
+ *
+ * @param $data
+ *   Data to decode.
+ * @return
+ *  Decoded data.
+ */
+function db_decode_blob($data) {
+  return $data;
+}
+
+/**
+ * Returns a properly formatted Character Large OBject value.
+ * In case of MS MSQL encodes data for insert or update into text field.
+ *
+ * @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) ? "'". str_replace("'", "''", $data) ."'" : "NULL";
+}
+
+/**
+ * Returns text from a Character Large OBject value.
+ * In case of MS MSQL decodes data after select from bytea field.
+ *
+ * @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.
+ * Note: This function requires MS MSQL 7.2 or later.
+ */
+function db_escape_string($text) {
+  $text = str_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 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 . " = 0x" . db_encode_blob($value) . " WHERE " . $query;
+  $query = preg_replace_callback(ANSI_RESERVED_REGEXP, '_db_query_callback_ansi_reserved', $query);
+  $query = preg_replace_callback(MSSQL_RESERVED_REGEXP, '_db_query_callback_mssql_reserved', $query);
+  $query = preg_replace_callback(MSSQL_MAX30_REGEXP, '_db_query_callback_mssql_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);
+}
+
+/**
+ * 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 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 . " = '" . db_encode_clob($value) . "' WHERE " . $query;
+  $query = preg_replace_callback(ANSI_RESERVED_REGEXP, '_db_query_callback_ansi_reserved', $query);
+  $query = preg_replace_callback(MSSQL_RESERVED_REGEXP, '_db_query_callback_mssql_reserved', $query);
+  $query = preg_replace_callback(MSSQL_MAX30_REGEXP, '_db_query_callback_mssql_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);
+}
+
+/**
+ * Lock a table.
+ * This function automatically starts a transaction.
+ */
+function db_lock_table($table) {
+  db_query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION; SELECT * FROM {' . db_escape_table($table) . '} WITH (HOLDLOCK)');
+}
+
+/**
+ * Unlock all locked tables.
+ * This function automatically commits a transaction.
+ */
+function db_unlock_tables() {
+  db_query('COMMIT TRANSACTION');
+}
+
+/**
+ * Check if a table exists.
+ */
+function db_table_exists($table) {
+  return db_result(db_query("SHOW TABLES LIKE '{". 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(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name LIKE '{" . db_escape_table($table) ."}' AND column_name LIKE '" . $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(MAX)',
+    'text:small'      => 'VARCHAR(MAX)',
+    'text:medium'     => 'VARCHAR(MAX)',
+    'text:big'        => 'VARCHAR(MAX)',
+    'text:normal'     => 'VARCHAR(MAX)',
+
+    'int:tiny'        => 'TINYINT',
+    'int:small'       => 'SMALLINT',
+    'int:medium'      => 'INT',
+    'int:big'         => 'BIGINT',
+    'int:normal'      => 'INT',
+
+    'float:tiny'      => 'FLOAT',
+    'float:small'     => 'FLOAT',
+    'float:medium'    => 'FLOAT',
+    'float:big'       => 'REAL',
+    'float:normal'    => 'FLOAT',
+
+    'numeric:normal'  => 'NUMERIC',
+
+    'blob:big'        => 'VARBINARY(MAX)',
+    'blob:normal'     => 'VARBINARY(MAX)',
+
+    'clob:big'        => 'VARCHAR(MAX)',
+    'clob:normal'     => 'VARCHAR(MAX)',
+
+    'datetime:normal' => 'TIMESTAMP',
+
+    'serial:tiny'     => 'TINYINT',
+    'serial:small'    => 'SMALLINT',
+    'serial:medium'   => 'INT',
+    'serial:big'      => 'INT',
+    'serial:normal'   => 'INT',
+  );
+  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_fields = array();
+  foreach ($table['fields'] as $field_name => $field) {
+    $sql_fields[] = _db_create_field_sql($field_name, _db_process_field($field));
+  }
+
+  $sql_keys = array();
+  if (isset($table['primary key']) && is_array($table['primary key'])) {
+    $sql_keys[] = 'CONSTRAINT pk_{' . $name . '} PRIMARY KEY (' . implode(', ', $table['primary key']) . ')';
+  }
+  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 = "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) {
+      $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['mssql_type'])) {
+    $map = db_type_map();
+    $field['mssql_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['mssql_type'];
+
+  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 .= ' IDENTITY (1, 1)';
+  }
+
+  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('EXEC sp_rename {'. $table .'}, {'. $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 COLUMN '. $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 COLUMN '. $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 COLUMN '. $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 CONSTRAINT pk_{' . $table . '}');
+}
+
+/**
+ * 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 CONSTRAINT '. $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) {
+  $ret[] = update_sql("EXEC sp_rename {" . $table . "}." . $field . ", old_" . $field);
+  $not_null = isset($spec['not null']) ? $spec['not null'] : FALSE;
+  unset($spec['not null']);
+  db_add_field($ret, $table, "$field_new", $spec);
+  $ret[] = update_sql("UPDATE {". $table ."} SET $field_new = old_". $field);
+  if ($not_null) {
+    $ret[] = update_sql("ALTER TABLE {". $table ."} ALTER $field_new SET NOT NULL");
+  }
+  db_drop_field($ret, $table, 'old_' . $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 drupal-6.x-dev-200708141658/includes/database.mysql-common.inc drupal-6.x-dev-mssql-0.1/includes/database.mysql-common.inc
--- drupal-6.x-dev-200708141658/includes/database.mysql-common.inc	2007-08-07 16:39:35.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/database.mysql-common.inc	2007-08-14 17:52:58.000000000 +0800
@@ -26,7 +26,11 @@
  *   you may also pass a single array containing the query arguments.
  *
  *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
- *   in '') and %%.
+ *   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.
@@ -216,6 +220,9 @@ function db_type_map() {
     'blob:big'        => 'LONGBLOB',
     'blob:normal'     => 'BLOB',
 
+    'clob:big'        => 'LONGTEXT',
+    'clob:normal'     => 'TEXT',
+
     'datetime:normal' => 'DATETIME',
   );
   return $map;
@@ -461,5 +468,91 @@ function db_last_insert_id($table, $fiel
 }
 
 /**
+ * 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 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);
+
+  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 = 'UPDATE ' . $table . ' SET ' . $column . ' = ' . db_encode_blob($value) . ' WHERE ' . $query;
+  return _db_query($query);
+}
+
+/**
+ * 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 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);
+
+  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 = 'UPDATE ' . $table . ' SET ' . $column . ' = ' . db_encode_clob($value) . ' WHERE ' . $query;
+  return _db_query($query);
+}
+
+/**
  * @} End of "ingroup schemaapi".
- */
\ No newline at end of file
+ */
diff -urpN drupal-6.x-dev-200708141658/includes/database.mysql.inc drupal-6.x-dev-mssql-0.1/includes/database.mysql.inc
--- drupal-6.x-dev-200708141658/includes/database.mysql.inc	2007-08-12 23:55:35.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/database.mysql.inc	2007-08-14 17:52:58.000000000 +0800
@@ -242,7 +242,11 @@ function db_affected_rows() {
  *   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 '') and %%.
+ *   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.
@@ -291,7 +295,11 @@ function db_query_range($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 '') and %%.
+ *   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.
@@ -323,11 +331,11 @@ function db_query_temporary($query) {
  * @param $data
  *   Data to encode.
  * @return
- *  Encoded data.
+ *  Encoded data, or empty LOB value placeholder for NULL $data.
  */
 function db_encode_blob($data) {
   global $active_db;
-  return "'". mysql_real_escape_string($data, $active_db) ."'";
+  return !is_null($data) ? "'". mysql_real_escape_string($data, $active_db) ."'" : "''";
 }
 
 /**
@@ -343,6 +351,31 @@ function db_decode_blob($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) {
+  global $active_db;
+  return !is_null($data) ? "'". mysql_real_escape_string($data, $active_db) ."'" : "''";
+}
+
+/**
+ * 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) {
diff -urpN drupal-6.x-dev-200708141658/includes/database.mysqli.inc drupal-6.x-dev-mssql-0.1/includes/database.mysqli.inc
--- drupal-6.x-dev-200708141658/includes/database.mysqli.inc	2007-08-12 23:55:35.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/database.mysqli.inc	2007-08-14 17:52:58.000000000 +0800
@@ -241,7 +241,11 @@ function db_affected_rows() {
  *   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 '') and %%.
+ *   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.
@@ -290,7 +294,11 @@ function db_query_range($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 '') and %%.
+ *   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.
@@ -322,11 +330,11 @@ function db_query_temporary($query) {
  * @param $data
  *   Data to encode.
  * @return
- *  Encoded data.
+ *  Encoded data, or empty LOB value placeholder for NULL $data.
  */
 function db_encode_blob($data) {
   global $active_db;
-  return "'". mysqli_real_escape_string($active_db, $data) ."'";
+  return !is_null($data) ? "'". mysqli_real_escape_string($active_db, $data) ."'" :  "''";
 }
 
 /**
@@ -342,6 +350,31 @@ function db_decode_blob($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) {
+  global $active_db;
+  return !is_null($data) ? "'". mysqli_real_escape_string($active_db, $data) ."'" :  "''";
+}
+
+/**
+ * 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) {
diff -urpN drupal-6.x-dev-200708141658/includes/database.pgsql.inc drupal-6.x-dev-mssql-0.1/includes/database.pgsql.inc
--- drupal-6.x-dev-200708141658/includes/database.pgsql.inc	2007-08-12 23:55:35.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/database.pgsql.inc	2007-08-14 17:52:58.000000000 +0800
@@ -120,7 +120,11 @@ function db_connect($url) {
  *   you may also pass a single array containing the query arguments.
  *
  *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
- *   in '') and %%.
+ *   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.
@@ -270,7 +274,11 @@ function db_affected_rows() {
  *   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 '') and %%.
+ *   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.
@@ -319,7 +327,11 @@ function db_query_range($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 '') and %%.
+ *   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.
@@ -347,15 +359,15 @@ function db_query_temporary($query) {
 
 /**
  * Returns a properly formatted Binary Large OBject value.
- * In case of PostgreSQL encodes data for insert into bytea field.
+ * In case of PostgreSQL encodes data for insert or update into bytea field.
  *
  * @param $data
  *   Data to encode.
  * @return
- *  Encoded data.
+ *  Encoded data, or empty LOB value placeholder for NULL $data.
  */
 function db_encode_blob($data) {
-  return "'". pg_escape_bytea($data) ."'";
+  return !is_null($data) ? "'". pg_escape_bytea($data) ."'" : "''";
 }
 
 /**
@@ -372,6 +384,33 @@ function db_decode_blob($data) {
 }
 
 /**
+ * Returns a properly formatted Character Large OBject value.
+ * In case of PostgreSQL encodes data for insert or update into text field.
+ *
+ * @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) ? "'". pg_escape_string($data) ."'" : "''";
+
+}
+
+/**
+ * Returns text from a Character Large OBject value.
+ * In case of PostgreSQL decodes data after select from bytea field.
+ *
+ * @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.
  * Note: This function requires PostgreSQL 7.2 or later.
  */
@@ -380,6 +419,92 @@ function db_escape_string($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 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);
+
+  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 = 'UPDATE ' . $table . ' SET ' . $column . ' = ' . db_encode_blob($value) . ' WHERE ' . $query;
+  return _db_query($query);
+}
+
+/**
+ * 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 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);
+
+  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 = 'UPDATE ' . $table . ' SET ' . $column . ' = ' . db_encode_clob($value) . ' WHERE ' . $query;
+  return _db_query($query);
+}
+
+/**
  * Lock a table.
  * This function automatically starts a transaction.
  */
@@ -483,6 +608,9 @@ function db_type_map() {
     'blob:big' => 'bytea',
     'blob:normal' => 'bytea',
 
+    'clob:big' => 'text',
+    'clob:normal' => 'text',
+
     'datetime:normal' => 'timestamp',
 
     'serial:tiny' => 'serial',
diff -urpN drupal-6.x-dev-200708141658/includes/install.inc drupal-6.x-dev-mssql-0.1/includes/install.inc
--- drupal-6.x-dev-200708141658/includes/install.inc	2007-07-31 03:22:47.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/install.inc	2007-08-14 18:21:56.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', 'mssql') as $type) {
     if (file_exists('./includes/install.'. $type .'.inc')) {
       include_once './includes/install.'. $type .'.inc';
       $function = $type .'_is_available';
diff -urpN drupal-6.x-dev-200708141658/includes/install.mssql.inc drupal-6.x-dev-mssql-0.1/includes/install.mssql.inc
--- drupal-6.x-dev-200708141658/includes/install.mssql.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/install.mssql.inc	2007-08-15 18:23:23.000000000 +0800
@@ -0,0 +1,131 @@
+<?php
+// $Id$
+
+// MS SQL specific install functions
+
+/**
+ * Check if MS SQL is available.
+ *
+ * @return
+ *  TRUE/FALSE
+ */
+function mssql_is_available() {
+  return function_exists('mssql_connect');
+}
+
+/**
+ * Check if we can connect to MS SQL.
+ *
+ * @return
+ *  TRUE/FALSE
+ */
+function drupal_test_mssql($url, &$success) {
+  if (!mssql_is_available()) {
+    drupal_set_message(st('PHP MS SQL support not enabled.'), 'error');
+    return FALSE;
+  }
+
+  $url = parse_url($url);
+
+  // Decode url-encoded information in the db connection string.
+  $url['user'] = urldecode($url['user']);
+  $url['pass'] = urldecode($url['pass']);
+  $url['host'] = urldecode($url['host']);
+  $url['path'] = urldecode($url['path']);
+
+  // Build mssql connection string and allow for non-standard MS SQL port.
+  $url['host'] = isset($url['port']) ? $url['host'] . ',' . $url['port'] : $url['host'];
+
+  // Test connecting to the database.
+  $connection = mssql_connect($url['host'], $url['user'], $url['pass']);
+  if (!$connection || !mssql_select_db(substr($url['path'], 1))) {
+    drupal_set_message(st('Failure to connect to your MS SQL database server. MS SQL 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' => 'Connection failed. See log file for failure reason')), 'error');
+    return FALSE;
+  }
+
+  $success = array('CONNECT');
+
+  // Test CREATE.
+  $query = 'CREATE TABLE drupal_install_test (id integer NOT NULL)';
+  $result = mssql_query($query, $connection);
+  if (!$result && $error = mssql_get_last_message()) {
+    drupal_set_message(st('We were unable to create a test table on your MS SQL database server with the command %query. MS SQL reports the following message: %error.<ul><li>Are you sure the configured username has the necessary MS SQL 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;
+  }
+  $err = FALSE;
+  $success[] = 'SELECT';
+  $success[] = 'CREATE';
+
+  // Test INSERT.
+  $query = 'INSERT INTO drupal_install_test (id) VALUES (1)';
+  $result = mssql_query($query, $connection);
+  if (!$result && $error = mssql_get_last_message()) {
+    drupal_set_message(st('We were unable to insert a value into a test table on your MS SQL database server. We tried inserting a value with the command %query and MS SQL 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';
+  $result = mssql_query($query, $connection);
+  if (!$result && $error = mssql_get_last_message()) {
+    drupal_set_message(st('We were unable to update a value in a test table on your MS SQL database server. We tried updating a value with the command %query and MS SQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'UPDATE';
+  }
+
+  // Test LOCK.
+  $query = 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION; SELECT * FROM drupal_install_test WITH (HOLDLOCK)';
+  $result = mssql_query($query, $connection);
+  if (!$result && $error = mssql_get_last_message()) {
+    drupal_set_message(st('We were unable to lock a test table on your MS SQL database server. We tried locking a table with the command %query and MS SQL 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 MS SQL
+  $query = 'COMMIT TRANSACTION';
+  $result = mssql_query($query, $connection);
+  if (!$result && $error = mssql_get_last_message()) {
+    drupal_set_message(st('We were unable to unlock a test table on your MS SQL database server. We tried unlocking a table with the command %query and MS SQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'UNLOCK';
+  }
+
+  // Test DELETE.
+  $query = 'DELETE FROM drupal_install_test';
+  $result = mssql_query($query, $connection);
+  if (!$result && $error = mssql_get_last_message()) {
+    drupal_set_message(st('We were unable to delete a value from a test table on your MS SQL database server. We tried deleting a value with the command %query and MS SQL reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'DELETE';
+  }
+
+  // Test DROP.
+  $query = 'DROP TABLE drupal_install_test';
+  $result = mssql_query($query, $connection);
+  if (!$result && $error = mssql_get_last_message()) {
+    drupal_set_message(st('We were unable to drop a test table from your MS SQL database server. We tried dropping a table with the command %query and MS SQL reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'DROP';
+  }
+
+  if ($err) {
+    return FALSE;
+  }
+
+  mssql_close($connection);
+  return TRUE;
+}
diff -urpN drupal-6.x-dev-200708141658/includes/variable-install.inc drupal-6.x-dev-mssql-0.1/includes/variable-install.inc
--- drupal-6.x-dev-200708141658/includes/variable-install.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/variable-install.inc	2007-08-14 17:53:07.000000000 +0800
@@ -0,0 +1,46 @@
+<?php
+// $Id$
+
+/**
+ * A stub variable implementation to be used during the installation
+ * process when database access is not yet available. Because Drupal's
+ * variable system never requires that saved data be present, these
+ * stub functions cache those informaton before table exists. Obviously,
+ * using this variable implementation during normal operations would have
+ * a negative impact on performance.
+ */
+
+function variable_get($name, $default) {
+  global $conf;
+
+  return isset($conf[$name]) ? $conf[$name] : $default;
+}
+
+function variable_set($name, $value) {
+  global $conf;
+
+  $conf[$name] = $value;
+}
+
+function variable_del($name) {
+  global $conf;
+
+  unset($conf[$name]);
+}
+
+function _variable_install_shutdown() {
+  global $conf;
+
+  if (function_exists('db_table_exists') && db_table_exists('variable')) {
+    foreach ($conf as $name => $value) {
+      $serialized_value = serialize($value);
+      db_query("UPDATE {variable} SET value = '%s' WHERE name = '%s'", $serialized_value, $name);
+      if (!db_affected_rows()) {
+        @db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, $serialized_value);
+      }
+    }
+  }
+
+  cache_clear_all('variables', 'cache');
+}
+register_shutdown_function('_variable_install_shutdown');
diff -urpN drupal-6.x-dev-200708141658/includes/variable.inc drupal-6.x-dev-mssql-0.1/includes/variable.inc
--- drupal-6.x-dev-200708141658/includes/variable.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/includes/variable.inc	2007-08-14 17:53:06.000000000 +0800
@@ -0,0 +1,56 @@
+<?php
+// $Id$
+
+/**
+ * Return a persistent variable.
+ *
+ * @param $name
+ *   The name of the variable to return.
+ * @param $default
+ *   The default value to use if this variable has never been set.
+ * @return
+ *   The value of the variable.
+ */
+function variable_get($name, $default) {
+  global $conf;
+
+  return isset($conf[$name]) ? $conf[$name] : $default;
+}
+
+/**
+ * Set a persistent variable.
+ *
+ * @param $name
+ *   The name of the variable to set.
+ * @param $value
+ *   The value to set. This can be any PHP data type; these functions take care
+ *   of serialization as necessary.
+ */
+function variable_set($name, $value) {
+  global $conf;
+
+  $serialized_value = serialize($value);
+  db_query("UPDATE {variable} SET value = '%s' WHERE name = '%s'", $serialized_value, $name);
+  if (!db_affected_rows()) {
+    @db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, $serialized_value);
+  }
+
+  cache_clear_all('variables', 'cache');
+
+  $conf[$name] = $value;
+}
+
+/**
+ * Unset a persistent variable.
+ *
+ * @param $name
+ *   The name of the variable to undefine.
+ */
+function variable_del($name) {
+  global $conf;
+
+  db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
+  cache_clear_all('variables', 'cache');
+
+  unset($conf[$name]);
+}
diff -urpN drupal-6.x-dev-200708141658/install.php drupal-6.x-dev-mssql-0.1/install.php
--- drupal-6.x-dev-200708141658/install.php	2007-07-26 01:35:47.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/install.php	2007-08-14 17:53:07.000000000 +0800
@@ -16,6 +16,13 @@ require_once './includes/install.inc';
  */
 function install_main() {
   global $profile, $install_locale, $conf;
+
+  // Since maybe no persistent storage is available yet, and functions that
+  // check for variables will fail, we temporarily replace the normal
+  // variable system with a stubbed-out version that cache variables until
+  // storage exists.
+  require_once './includes/variable-install.inc';
+
   require_once './includes/bootstrap.inc';
   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
 
diff -urpN drupal-6.x-dev-200708141658/modules/aggregator/aggregator.module drupal-6.x-dev-mssql-0.1/modules/aggregator/aggregator.module
--- drupal-6.x-dev-200708141658/modules/aggregator/aggregator.module	2007-08-13 00:12:00.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/aggregator/aggregator.module	2007-08-14 17:52:58.000000000 +0800
@@ -979,15 +979,17 @@ function aggregator_parse_feed(&$data, $
 
 function aggregator_save_item($edit) {
   if ($edit['iid'] && $edit['title']) {
-    db_query("UPDATE {aggregator_item} SET title = '%s', link = '%s', author = '%s', description = '%s', guid = '%s', timestamp = %d WHERE iid = %d", $edit['title'], $edit['link'], $edit['author'], $edit['description'], $edit['guid'], $edit['timestamp'], $edit['iid']);
+    db_query("UPDATE {aggregator_item} SET title = '%s', link = '%s', author = '%s', description = %c, guid = '%s', timestamp = %d WHERE iid = %d", $edit['title'], $edit['link'], $edit['author'], NULL, $edit['guid'], $edit['timestamp'], $edit['iid']);
+    db_update_clob('iid = %d', $edit['iid'], db_prefix_tables('{aggregator_item}'), 'description', $edit['description']);
   }
   else if ($edit['iid']) {
     db_query('DELETE FROM {aggregator_item} WHERE iid = %d', $edit['iid']);
     db_query('DELETE FROM {aggregator_category_item} WHERE iid = %d', $edit['iid']);
   }
   else if ($edit['title'] && $edit['link']) {
-    db_query("INSERT INTO {aggregator_item} (fid, title, link, author, description, timestamp, guid) VALUES (%d, '%s', '%s', '%s', '%s', %d, '%s')", $edit['fid'], $edit['title'], $edit['link'], $edit['author'], $edit['description'], $edit['timestamp'], $edit['guid']);
+    db_query("INSERT INTO {aggregator_item} (fid, title, link, author, description, timestamp, guid) VALUES (%d, '%s', '%s', '%s', %c, %d, '%s')", $edit['fid'], $edit['title'], $edit['link'], $edit['author'], NULL, $edit['timestamp'], $edit['guid']);
     $edit['iid'] = db_last_insert_id('aggregator_item', 'iid');
+    db_update_clob('iid = %d', $edit['iid'], db_prefix_tables('{aggregator_item}'), 'description', $edit['description']);
     // file the items in the categories indicated by the feed
     $categories = db_query('SELECT cid FROM {aggregator_category_feed} WHERE fid = %d', $edit['fid']);
     while ($category = db_fetch_object($categories)) {
@@ -1234,6 +1236,7 @@ function aggregator_page_rss() {
   }
 
   while ($item = db_fetch_object($result)) {
+    $item->description = db_decode_clob($item->description);
     switch (variable_get('feed_item_length', 'teaser')) {
       case 'teaser':
         $teaser = node_teaser($item->description);
@@ -1409,7 +1412,7 @@ function theme_aggregator_page_item($ite
   $output .= "<div class=\"feed-item-meta\">$source <span class=\"feed-item-date\">$source_date</span></div>\n";
 
   if ($item->description) {
-    $output .= '<div class="feed-item-body">'. aggregator_filter_xss($item->description) ."</div>\n";
+    $output .= '<div class="feed-item-body">'. aggregator_filter_xss(db_decode_clob($item->description)) ."</div>\n";
   }
 
   $result = db_query('SELECT c.title, c.cid FROM {aggregator_category_item} ci LEFT JOIN {aggregator_category} c ON ci.cid = c.cid WHERE ci.iid = %d ORDER BY c.title', $item->iid);
diff -urpN drupal-6.x-dev-200708141658/modules/aggregator/aggregator.schema drupal-6.x-dev-mssql-0.1/modules/aggregator/aggregator.schema
--- drupal-6.x-dev-200708141658/modules/aggregator/aggregator.schema	2007-07-15 18:09:21.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/aggregator/aggregator.schema	2007-08-14 17:52:58.000000000 +0800
@@ -57,7 +57,7 @@ function aggregator_schema() {
       'title'       => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
       'link'        => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
       'author'      => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
-      'description' => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
+      'description' => array('type' => 'clob', 'not null' => TRUE, 'size' => 'big'),
       'timestamp'   => array('type' => 'int', 'not null' => FALSE),
       'guid'        => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE)
     ),
diff -urpN drupal-6.x-dev-200708141658/modules/block/block.admin.inc drupal-6.x-dev-mssql-0.1/modules/block/block.admin.inc
--- drupal-6.x-dev-200708141658/modules/block/block.admin.inc	2007-08-12 23:55:35.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/block/block.admin.inc	2007-08-14 17:52:58.000000000 +0800
@@ -257,8 +257,9 @@ function block_add_block_form_validate($
  * Save the new custom block.
  */
 function block_add_block_form_submit($form, &$form_state) {
-  db_query("INSERT INTO {boxes} (body, info, format) VALUES  ('%s', '%s', %d)", $form_state['values']['body'], $form_state['values']['info'], $form_state['values']['format']);
+  db_query("INSERT INTO {boxes} (body, info, format) VALUES  (%c, '%s', %d)", NULL, $form_state['values']['info'], $form_state['values']['format']);
   $delta = db_last_insert_id('boxes', 'bid');
+  db_update_clob('bid = %d', $delta, db_prefix_tables('{boxes}'), 'body', $form_state['values']['body']);
 
   foreach (list_themes() as $key => $theme) {
     if ($theme->status) {
diff -urpN drupal-6.x-dev-200708141658/modules/block/block.module drupal-6.x-dev-mssql-0.1/modules/block/block.module
--- drupal-6.x-dev-200708141658/modules/block/block.module	2007-07-25 02:17:30.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/block/block.module	2007-08-14 17:52:58.000000000 +0800
@@ -206,7 +206,7 @@ function block_block($op = 'list', $delt
 
     case 'view':
       $block = db_fetch_object(db_query('SELECT body, format FROM {boxes} WHERE bid = %d', $delta));
-      $data['content'] = check_markup($block->body, $block->format, FALSE);
+      $data['content'] = check_markup(db_decode_clob($block->body), $block->format, FALSE);
       return $data;
   }
 }
@@ -321,7 +321,8 @@ function block_box_save($edit, $delta) {
     $edit['format'] = FILTER_FORMAT_DEFAULT;
   }
 
-  db_query("UPDATE {boxes} SET body = '%s', info = '%s', format = %d WHERE bid = %d", $edit['body'], $edit['info'], $edit['format'], $delta);
+  db_query("UPDATE {boxes} SET body = %c, info = '%s', format = %d WHERE bid = %d", NULL, $edit['info'], $edit['format'], $delta);
+  db_update_clob('bid = %d', $delta, db_prefix_tables('{boxes}'), 'body', $edit['body']);
 
   return TRUE;
 }
diff -urpN drupal-6.x-dev-200708141658/modules/block/block.schema drupal-6.x-dev-mssql-0.1/modules/block/block.schema
--- drupal-6.x-dev-200708141658/modules/block/block.schema	2007-05-25 20:46:43.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/block/block.schema	2007-08-14 17:52:58.000000000 +0800
@@ -36,7 +36,7 @@ function block_schema() {
   $schema['boxes'] = array(
     'fields' => array(
       'bid'    => array('type' => 'serial', 'not null' => TRUE),
-      'body'   => array('type' => 'text', 'not null' => FALSE, 'size' => 'big'),
+      'body'   => array('type' => 'clob', 'not null' => FALSE, 'size' => 'big'),
       'info'   => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''),
       'format' => array('type' => 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0)
     ),
diff -urpN drupal-6.x-dev-200708141658/modules/comment/comment.module drupal-6.x-dev-mssql-0.1/modules/comment/comment.module
--- drupal-6.x-dev-200708141658/modules/comment/comment.module	2007-08-13 00:12:00.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/comment/comment.module	2007-08-14 17:52:58.000000000 +0800
@@ -474,7 +474,7 @@ function comment_nodeapi(&$node, $op, $a
       $text = '';
       $comments = db_query('SELECT subject, comment, format FROM {comments} WHERE nid = %d AND status = %d', $node->nid, COMMENT_PUBLISHED);
       while ($comment = db_fetch_object($comments)) {
-        $text .= '<h2>'. check_plain($comment->subject) .'</h2>'. check_markup($comment->comment, $comment->format, FALSE);
+        $text .= '<h2>'. check_plain($comment->subject) .'</h2>'. check_markup(db_decode_clob($comment->comment), $comment->format, FALSE);
       }
       return $text;
 
@@ -731,7 +731,8 @@ function comment_save($edit) {
     if (!form_get_errors()) {
       if ($edit['cid']) {
         // Update the comment in the database.
-        db_query("UPDATE {comments} SET status = %d, timestamp = %d, subject = '%s', comment = '%s', format = %d, uid = %d, name = '%s', mail = '%s', homepage = '%s' WHERE cid = %d", $edit['status'], $edit['timestamp'], $edit['subject'], $edit['comment'], $edit['format'], $edit['uid'], $edit['name'], $edit['mail'], $edit['homepage'], $edit['cid']);
+        db_query("UPDATE {comments} SET status = %d, timestamp = %d, subject = '%s', comment = %c, format = %d, uid = %d, name = '%s', mail = '%s', homepage = '%s' WHERE cid = %d", $edit['status'], $edit['timestamp'], $edit['subject'], NULL, $edit['format'], $edit['uid'], $edit['name'], $edit['mail'], $edit['homepage'], $edit['cid']);
+        db_update_clob('cid = %d', $edit['cid'], db_prefix_tables('{comments}'), 'comment', $edit['comment']);
 
         _comment_update_node_statistics($edit['nid']);
 
@@ -796,8 +797,9 @@ function comment_save($edit) {
         }
 
         $edit += array('mail' => '', 'homepage' => '');
-        db_query("INSERT INTO {comments} (nid, pid, uid, subject, comment, format, hostname, timestamp, status, thread, name, mail, homepage) VALUES (%d, %d, %d, '%s', '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s')", $edit['nid'], $edit['pid'], $edit['uid'], $edit['subject'], $edit['comment'], $edit['format'], ip_address(), $edit['timestamp'], $status, $thread, $edit['name'], $edit['mail'], $edit['homepage']);
+        db_query("INSERT INTO {comments} (nid, pid, uid, subject, comment, format, hostname, timestamp, status, thread, name, mail, homepage) VALUES (%d, %d, %d, '%s', %c, %d, '%s', %d, %d, '%s', '%s', '%s', '%s')", $edit['nid'], $edit['pid'], $edit['uid'], $edit['subject'], NULL, $edit['format'], ip_address(), $edit['timestamp'], $status, $thread, $edit['name'], $edit['mail'], $edit['homepage']);
         $edit['cid'] = db_last_insert_id('comments', 'cid');
+        db_update_clob('cid = %d', $edit['cid'], db_prefix_tables('{comments}'), 'comment', $edit['comment']);
 
         _comment_update_node_statistics($edit['nid']);
 
@@ -1203,7 +1205,7 @@ function comment_admin_overview($type = 
   while ($comment = db_fetch_object($result)) {
     $comments[$comment->cid] = '';
     $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
-    $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('title' => truncate_utf8($comment->comment, 128), 'fragment' => 'comment-'. $comment->cid)));
+    $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('title' => truncate_utf8(db_decode_clob($comment->comment), 128), 'fragment' => 'comment-'. $comment->cid)));
     $form['username'][$comment->cid] = array('#value' => theme('username', $comment));
     $form['timestamp'][$comment->cid] = array('#value' => format_date($comment->timestamp, 'small'));
     $form['operations'][$comment->cid] = array('#value' => l(t('edit'), 'comment/edit/'. $comment->cid, array('query' => $destination)));
@@ -1733,7 +1735,7 @@ function theme_comment_view($comment, $n
 
   // Switch to folded/unfolded view of the comment
   if ($visible) {
-    $comment->comment = check_markup($comment->comment, $comment->format, FALSE);
+    $comment->comment = check_markup(db_decode_clob($comment->comment), $comment->format, FALSE);
 
     // Comment API hook
     comment_invoke_comment($comment, 'view');
@@ -2185,7 +2187,7 @@ function comment_unpublish_by_keyword_ac
  */
 function comment_unpublish_by_keyword_action($comment, $context) {
   foreach ($context['keywords'] as $keyword) {
-    if (strstr($comment->comment, $keyword) || strstr($comment->subject, $keyword)) {
+    if (strstr(db_decode_clob($comment->comment), $keyword) || strstr($comment->subject, $keyword)) {
       db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_NOT_PUBLISHED, $comment->cid);
       watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject));
       break;
diff -urpN drupal-6.x-dev-200708141658/modules/comment/comment.schema drupal-6.x-dev-mssql-0.1/modules/comment/comment.schema
--- drupal-6.x-dev-200708141658/modules/comment/comment.schema	2007-07-31 05:27:34.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/comment/comment.schema	2007-08-14 17:52:58.000000000 +0800
@@ -9,7 +9,7 @@ function comment_schema() {
       'nid'       => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
       'uid'       => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
       'subject'   => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => ''),
-      'comment'   => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
+      'comment'   => array('type' => 'clob', 'not null' => TRUE, 'size' => 'big'),
       'hostname'  => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''),
       'timestamp' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
       'status'    => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'size' => 'tiny'),
diff -urpN drupal-6.x-dev-200708141658/modules/contact/contact.admin.inc drupal-6.x-dev-mssql-0.1/modules/contact/contact.admin.inc
--- drupal-6.x-dev-200708141658/modules/contact/contact.admin.inc	2007-07-16 20:43:05.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/contact/contact.admin.inc	2007-08-14 17:52:58.000000000 +0800
@@ -110,13 +110,16 @@ function contact_admin_edit_submit($form
   }
   $form_state['values']['recipients'] = implode(',', $recipients);
   if (empty($form_state['values']['cid']) || $form_state['values']['contact_op'] == 'add') {
-    db_query("INSERT INTO {contact} (category, recipients, reply, weight, selected) VALUES ('%s', '%s', '%s', %d, %d)", $form_state['values']['category'], $form_state['values']['recipients'], $form_state['values']['reply'], $form_state['values']['weight'], $form_state['values']['selected']);
+    db_query("INSERT INTO {contact} (category, recipients, reply, weight, selected) VALUES ('%s', '%s', %c, %d, %d)", $form_state['values']['category'], $form_state['values']['recipients'], NULL, $form_state['values']['weight'], $form_state['values']['selected']);
+    $cid = db_last_insert_id('contact', 'cid');
+    db_update_clob('cid = %d', $cid, db_prefix_tables('{contact}'), 'reply', $form_state['values']['reply']);
     drupal_set_message(t('Category %category has been added.', array('%category' => $form_state['values']['category'])));
     watchdog('mail', 'Contact form: category %category added.', array('%category' => $form_state['values']['category']), WATCHDOG_NOTICE, l(t('view'), 'admin/build/contact'));
 
   }
   else {
-    db_query("UPDATE {contact} SET category = '%s', recipients = '%s', reply = '%s', weight = %d, selected = %d WHERE cid = %d", $form_state['values']['category'], $form_state['values']['recipients'], $form_state['values']['reply'], $form_state['values']['weight'], $form_state['values']['selected'], $form_state['values']['cid']);
+    db_query("UPDATE {contact} SET category = '%s', recipients = '%s', reply = %c, weight = %d, selected = %d WHERE cid = %d", $form_state['values']['category'], $form_state['values']['recipients'], NULL, $form_state['values']['weight'], $form_state['values']['selected'], $form_state['values']['cid']);
+    db_update_clob('cid = %d', $form_state['values']['cid'], db_prefix_tables('{contact}'), 'reply', $form_state['values']['reply']);
     drupal_set_message(t('Category %category has been updated.', array('%category' => $form_state['values']['category'])));
     watchdog('mail', 'Contact form: category %category updated.', array('%category' => $form_state['values']['category']), WATCHDOG_NOTICE, l(t('view'), 'admin/build/contact'));
   }
diff -urpN drupal-6.x-dev-200708141658/modules/contact/contact.module drupal-6.x-dev-mssql-0.1/modules/contact/contact.module
--- drupal-6.x-dev-200708141658/modules/contact/contact.module	2007-07-16 14:37:49.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/contact/contact.module	2007-08-14 17:52:58.000000000 +0800
@@ -171,7 +171,7 @@ function contact_mail($key, &$message, $
     case 'page_autoreply':
       $contact = $params['contact'];
       $message['subject'] .= t('[!category] !subject', array('!category' => $contact['category'], '!subject' => $params['subject']), $language->language);
-      $message['body'][] = $contact['reply'];
+      $message['body'][] = db_decode_clob($contact['reply']);
       break;
     case 'user_mail':
     case 'user_copy':
diff -urpN drupal-6.x-dev-200708141658/modules/contact/contact.pages.inc drupal-6.x-dev-mssql-0.1/modules/contact/contact.pages.inc
--- drupal-6.x-dev-200708141658/modules/contact/contact.pages.inc	2007-08-12 00:13:45.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/contact/contact.pages.inc	2007-08-14 17:52:58.000000000 +0800
@@ -132,7 +132,7 @@ function contact_mail_page_submit($form,
   }
 
   // Send an auto-reply if necessary using the current language.
-  if ($contact['reply']) {
+  if (db_decode_clob($contact['reply'])) {
     drupal_mail('contact', 'page_autoreply', $from, $language, $values, $contact['recipients']);
   }
 
diff -urpN drupal-6.x-dev-200708141658/modules/contact/contact.schema drupal-6.x-dev-mssql-0.1/modules/contact/contact.schema
--- drupal-6.x-dev-200708141658/modules/contact/contact.schema	2007-07-15 18:09:21.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/contact/contact.schema	2007-08-14 17:52:58.000000000 +0800
@@ -7,7 +7,7 @@ function contact_schema() {
       'cid'        => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
       'category'   => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
       'recipients' => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
-      'reply'      => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
+      'reply'      => array('type' => 'clob', 'not null' => TRUE, 'size' => 'big'),
       'weight'     => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'tiny'),
       'selected'   => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'tiny')
     ),
diff -urpN drupal-6.x-dev-200708141658/modules/node/node.module drupal-6.x-dev-mssql-0.1/modules/node/node.module
--- drupal-6.x-dev-200708141658/modules/node/node.module	2007-08-13 00:12:00.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/node/node.module	2007-08-14 17:52:58.000000000 +0800
@@ -638,6 +638,8 @@ function node_load($param = array(), $re
   else {
     $node = db_fetch_object(db_query('SELECT n.nid, n.vid, n.type, n.status, n.language, n.created, n.changed, n.comment, n.promote, n.sticky, n.tnid, n.translate, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.log, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.vid = n.vid WHERE '. $cond, $arguments));
   }
+  $node->body = db_decode_clob($node->body);
+  $node->teaser = db_decode_clob($node->teaser);
 
   if ($node && $node->nid) {
     // Call the node specific callback (if any) and piggy-back the
@@ -698,12 +700,12 @@ function node_save(&$node) {
 
   // Split off revisions data to another structure
   $revisions_table_values = array('nid' => &$node->nid,
-                     'title' => $node->title, 'body' => isset($node->body) ? $node->body : '',
-                     'teaser' => $node->teaser, 'timestamp' => $node->changed,
+                     'title' => $node->title, 'body' => NULL,
+                     'teaser' => NULL, 'timestamp' => $node->changed,
                      'uid' => $user->uid, 'format' => isset($node->format) ? $node->format : FILTER_FORMAT_DEFAULT);
   $revisions_table_types = array('nid' => '%d',
-                     'title' => "'%s'", 'body' => "'%s'",
-                     'teaser' => "'%s'", 'timestamp' => '%d',
+                     'title' => "'%s'", 'body' => "%c",
+                     'teaser' => "%c", 'timestamp' => '%d',
                      'uid' => '%d', 'format' => '%d');
   if (!empty($node->log) || $node->is_new || (isset($node->revision) && $node->revision)) {
     // Only store the log message if there's something to store; this prevents
@@ -732,6 +734,8 @@ function node_save(&$node) {
     $revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', $revisions_table_types) .')';
     db_query($revisions_query, $revisions_table_values);
     $node->vid = db_last_insert_id('node_revisions', 'vid');
+    db_update_clob('vid = %d', $node->vid, db_prefix_tables('{node_revisions}'), 'body', isset($node->body) ? $node->body : '');
+    db_update_clob('vid = %d', $node->vid, db_prefix_tables('{node_revisions}'), 'teaser', $node->teaser);
     $op = 'insert';
   }
   else {
@@ -746,6 +750,8 @@ function node_save(&$node) {
       $revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', $revisions_table_types) .')';
       db_query($revisions_query, $revisions_table_values);
       $node->vid = db_last_insert_id('node_revisions', 'vid');
+      db_update_clob('vid = %d', $node->vid, db_prefix_tables('{node_revisions}'), 'body', isset($node->body) ? $node->body : '');
+      db_update_clob('vid = %d', $node->vid, db_prefix_tables('{node_revisions}'), 'teaser', $node->teaser);
     }
     else {
       $arr = array();
@@ -755,6 +761,8 @@ function node_save(&$node) {
       $revisions_table_values[] = $node->vid;
       $revisions_query = 'UPDATE {node_revisions} SET '. implode(', ', $arr) .' WHERE vid = %d';
       db_query($revisions_query, $revisions_table_values);
+      db_update_clob('vid = %d', $node->vid, db_prefix_tables('{node_revisions}'), 'body', isset($node->body) ? $node->body : '');
+      db_update_clob('vid = %d', $node->vid, db_prefix_tables('{node_revisions}'), 'teaser', $node->teaser);
       $update_node = FALSE;
     }
     $op = 'update';
diff -urpN drupal-6.x-dev-200708141658/modules/node/node.schema drupal-6.x-dev-mssql-0.1/modules/node/node.schema
--- drupal-6.x-dev-200708141658/modules/node/node.schema	2007-08-10 19:14:22.000000000 +0800
+++ drupal-6.x-dev-mssql-0.1/modules/node/node.schema	2007-08-14 17:52:58.000000000 +0800
@@ -72,8 +72,8 @@ function node_schema() {
       'vid'       => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
       'uid'       => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
       'title'     => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''),
-      'body'      => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
-      'teaser'    => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
+      'body'      => array('type' => 'clob', 'not null' => TRUE, 'size' => 'big'),
+      'teaser'    => array('type' => 'clob', 'not null' => TRUE, 'size' => 'big'),
       'log'       => array('type' => 'text', 'not null' => TRUE, 'size' => 'big'),
       'timestamp' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
       'format'    => array('type' => 'int', 'not null' => TRUE, 'default' => 0)
