diff -urpN drupal-6.x-dev/includes/database.inc drupal-6.x-dev-oci8/includes/database.inc
--- drupal-6.x-dev/includes/database.inc	2007-11-16 23:58:08.000000000 +0800
+++ drupal-6.x-dev-oci8/includes/database.inc	2007-11-17 00:43:46.000000000 +0800
@@ -148,7 +148,7 @@ function db_set_active($name = 'default'
     else {
       drupal_maintenance_theme();
       drupal_set_title('Unsupported database type');
-      print theme('maintenance_page', '<p>The database type '. theme('placeholder', $db_type) .' is unsupported. Please use either <var>mysql</var> for MySQL 3.x &amp; 4.0.x databases, <var>mysqli</var> for MySQL 4.1.x+ databases, or <var>pgsql</var> for PostgreSQL databases. The database information is in your <code>settings.php</code> file.</p>
+      print theme('maintenance_page', '<p>The database type '. theme('placeholder', $db_type) .' is unsupported. Please use either <var>mysql</var> for MySQL 3.x &amp; 4.0.x databases, <var>mysqli</var> for MySQL 4.1.x+ databases, <var>pgsql</var> for PostgreSQL databases, or <var>oci8</var> for Oracle 10gR2+. The database information is in your <code>settings.php</code> file.</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;
     }
diff -urpN drupal-6.x-dev/includes/database.oci8.inc drupal-6.x-dev-oci8/includes/database.oci8.inc
--- drupal-6.x-dev/includes/database.oci8.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-oci8/includes/database.oci8.inc	2007-11-17 00:42:44.000000000 +0800
@@ -0,0 +1,1379 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Database interface code for Oracle database servers.
+ */
+
+/**
+ * @ingroup database
+ * @{
+ */
+
+/**
+ * Indicates the concatenation operator.
+ */
+define('DB_CONCAT_OPERATOR', '||');
+
+/**
+ * Indicates the name of the SQL strtoupper function.
+ */
+define('DB_UPPER', 'UPPER');
+
+/**
+ * Indicates the SQL to generate a random number between 0.00 and 1.00.
+ */
+define('DB_RAND', 'ABS(MOD(DBMS_RANDOM.RANDOM,10000001)/10000000)');
+
+/**
+ * Indicates the name of the SQL strlen function.
+ */
+define('DB_STRLEN', 'LENGTH');
+
+/**
+ * Indicates the name of the SQL substr function.
+ */
+define('DB_SUBSTR', 'SUBSTR');
+
+/**
+ * Return a portably concatenate strings.
+ *
+ * @param ...
+ *   Variable number of string parameters.
+ * @return
+ *   Portably concatenate strings.
+ */
+function db_concat() {
+  $args = func_get_args();
+  return implode(DB_CONCAT_OPERATOR, $args);
+}
+
+/**
+ * Returns a string that is the equivalent of MySQL IFNULL or Oracle NVL.
+ *
+ * @return
+ *   If $expr1 is not NULL, returns $expr1; otherwise it returns $expr2.
+ */
+function db_if_null($expr1, $expr2) {
+  return " NVL($expr1, $expr2) ";
+}
+
+/**
+ * Report database status.
+ */
+function db_status_report($phase) {
+  $t = get_t();
+
+  $version = db_version();
+
+  $form['oci8'] = array(
+    'title' => $t('Oracle database'),
+    'value' => $version,
+  );
+
+  if (version_compare($version, DRUPAL_MINIMUM_OCI) < 0) {
+    $form['oci8']['severity'] = REQUIREMENT_ERROR;
+    $form['oci8']['description'] = $t('Your Oracle Server is too old. Drupal requires at least Oracle %version.', array('%version' => DRUPAL_MINIMUM_OCI));
+  }
+
+  return $form;
+}
+
+/**
+ * Returns the version of the database server currently in use.
+ *
+ * @return Database server version
+ */
+function db_version() {
+  $banner = db_result(db_query('SELECT banner FROM sys.V_$VERSION'));
+  preg_match('/\s([0-9.]+)\s/', $banner, $version);
+  return array_pop($version);
+}
+
+/**
+ * Initialize a database connection.
+ */
+function db_connect($url) {
+
+  // Check if Oracle support is present in PHP 5
+  if (!(function_exists('oci_connect'))) {
+    drupal_maintenance_theme();
+    drupal_set_title('PHP Oracle support not enabled');
+    print theme('maintenance_page', '<p>We were unable to use the Oracle database because the Oracle extension for PHP is not installed. Check your <code>PHP.ini</code> to see how you can enable it.</p>');
+    //TODO: create a book page about Oracle support.
+    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 oci8 connection string and allow for non-standard Oracle port.
+  $conn_string = '//' . $url['host'] . (isset($url['port']) ? ':' . $url['port'] : '') . '/' . substr($url['path'], 1);
+
+  // Even we can indicate charset parameter for oci_connect, which will be
+  // used in the new connection, we will not use this feature. Therefore
+  // NLS_LANG environment variable will be used instead.
+  $connection = @oci_connect($url['user'], $url['pass'], $conn_string);
+
+  // Test connecting to the database.
+  if (!$connection && ($error = oci_error())) {
+    drupal_maintenance_theme();
+    drupal_set_header('HTTP/1.1 503 Service Unavailable');
+    drupal_set_title('Unable to connect to database');
+    print theme('maintenance_page', '<p>If you still have to install Drupal, proceed to the <a href="'. base_path() .'install.php">installation page</a>.</p>
+<p>If you have already finished installed Drupal, this either means that the username and password information in your <code>settings.php</code> file is incorrect or that we can\'t connect to the Oracle database server. This could mean your hosting provider\'s database server is down.</p>
+<p>The Oracle error was: '. theme('placeholder', $error['message']) .'.</p>
+<p>Currently, 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 Oracle 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('ORACLE_RESERVED_REGEXP', '/([[:space:],\.\(\)<>\+-=!*]|^)(access|audit|cluster|comment|compress|exclusive|file|identified|increment|index|initial|lock|long|maxextents|minus|mlslabel|mode|modify|noaudit|nocompress|nowait|number|offline|online|pctfree|raw|rename|resource|rowid|rownum|share|successful|synonym|sysdate|uid|validate|varchar2)([[:space:],\.\(\)<>\+-=!*]|$)/Ds');
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query_callback_oci8_reserved($match) {
+  return $match[1] . '"' . $match[2] . '"' . $match[3];
+}
+
+/**
+ * Regex for rewriting the Oracle'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('ORACLE_MAX30_REGEXP', '/([[:space:],\.\(\)<>\+-=!*]|^)([a-z0-9_]{31,})([[:space:],\.\(\)<>\+-=!*]|$)/Ds');
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query_callback_oci8_max30($match) {
+  // Try to fetch mapping, if not exists, set it up.
+  if (!($trim = variable_get('oci8_' . $match[2], NULL))) {
+    // Fetch last counter, and +1 for next mapping.
+    $count = variable_get('oci8_max30', 0) + 1;
+    variable_set('oci8_max30', $count);
+    // Setup mapping and reverse mapping.
+    $trim = substr($match[2], 0, 30 - strlen($count)) . $count;
+    variable_set('oci8_' . $match[2], $trim);
+    variable_set('oci8_' . $trim, $match[2]);
+  }
+  return $match[1] . $trim . $match[3];
+}
+
+/**
+ * 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) and %%.
+ *
+ *   NOTE: use NULL as arguments substitution for %b and %c. This will be
+ *   replaced as corresponding empty LOB value placeholder, based on the
+ *   database specific representation.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @return
+ *   A database query result resource, or FALSE if the query was not
+ *   executed correctly.
+ */
+function db_query($query) {
+  $args = func_get_args();
+  array_shift($args);
+  $query = db_prefix_tables($query);
+  $query = preg_replace_callback(ANSI_RESERVED_REGEXP, '_db_query_callback_ansi_reserved', $query);
+  $query = preg_replace_callback(ORACLE_RESERVED_REGEXP, '_db_query_callback_oci8_reserved', $query);
+  $query = preg_replace_callback(ORACLE_MAX30_REGEXP, '_db_query_callback_oci8_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;
+  }
+
+  // Perform actual query and return the result set.
+  $last_result = @oci_parse($active_db, $query);
+  $result = @oci_execute($last_result);
+
+  if (variable_get('dev_query', 0)) {
+    $bt = debug_backtrace();
+    $query = $bt[2]['function'] . "\n" . $query;
+    list($usec, $sec) = explode(' ', microtime());
+    $stop = (float)$usec + (float)$sec;
+    $diff = $stop - $timer;
+    $queries[] = array($query, $diff);
+  }
+
+  if ($debug) {
+    $error = oci_error($last_result);
+    print '<p>query: '. $query .'<br />error:'. $error['message'] .'</p>';
+  }
+
+  if ($last_result !== FALSE) {
+    return $last_result;
+  }
+  else {
+    $error = oci_error($last_result);
+    trigger_error(check_plain($error['message']."\nquery: ". $error['sqltext']), 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) {
+    if ($tmp = @oci_fetch_object($result)) {
+      $ret = new stdClass();
+      foreach ((array) $tmp as $key => $value) {
+        $column = $key;
+        // Remove all \" if required.
+        $column = str_replace("\"", '', $column);
+        // Force all column name into lowercase.
+        $column = strtolower($column);
+        // Preform short-to-long mapping if required.
+        $column = variable_get('oci8_' . $column, $column);
+        $ret->$column = $value;
+      }
+      return $ret;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Fetch one result row from the previous query as an array.
+ *
+ * @param $result
+ *   A database query result resource, as returned from db_query().
+ * @return
+ *   An associative array representing the next row of the result, or FALSE.
+ *   The keys of this object are the names of the table fields selected by the
+ *   query, and the values are the field values for this result row.
+ */
+function db_fetch_array($result) {
+  if ($result) {
+    if ($tmp = @oci_fetch_assoc($result)) {
+      $ret = array();
+      foreach ((array) $tmp as $key => $value) {
+        $column = $key;
+        // Remove all \" if required.
+        $column = str_replace("\"", '', $column);
+        // Force all column name into lowercase.
+        $column = strtolower($column);
+        // Preform short-to-long mapping if required.
+        $column = variable_get('oci8_' . $column, $column);
+        $ret[$column] = $value;
+      }
+      return $ret;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Return an individual result field from the previous query.
+ *
+ * Only use this function if exactly one field is being selected; otherwise,
+ * use db_fetch_object() or db_fetch_array().
+ *
+ * @param $result
+ *   A database query result resource, as returned from db_query().
+ * @return
+ *   The resulting field or FALSE.
+ */
+function db_result($result) {
+  if ($result && @oci_fetch($result)) {
+    if ($ret = @oci_result($result, 1)) {
+      return $ret;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Determine whether the previous query caused an error.
+ */
+function db_error() {
+  $error = oci_error();
+  return $error['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) {
+  return db_result(db_query("SELECT seq_{" . $table . "}_" . $field . ".currval FROM DUAL"));
+}
+
+/**
+ * Determine the number of rows changed by the preceding query.
+ */
+function db_affected_rows() {
+  global $last_result;
+  return oci_num_rows($last_result);
+}
+
+/**
+ * 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) 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 = 'SELECT * FROM (SELECT sub1.*, ROWNUM AS line2 FROM ('. $query .') sub1) WHERE line2 BETWEEN '. ($from + 1) .' AND '. ($from + $count);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  return db_query($query, $args);
+}
+
+/**
+ * Runs a SELECT query and stores its results in a temporary table.
+ *
+ * Use this as a substitute for db_query() when the results need to stored
+ * in a temporary table. Temporary tables exist for the duration of the page
+ * request.
+ * User-supplied arguments to the query should be passed in as separate parameters
+ * so that they can be properly escaped to avoid SQL injection attacks.
+ *
+ * Note that if you need to know how many results were returned, you should do
+ * a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() do
+ * not give consistent result across different database types in this case.
+ *
+ * @param $query
+ *   A string containing a normal SELECT SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. The query arguments can be enclosed in one
+ *   array instead.
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in ''), %c (character large object) and %%.
+ *
+ *   NOTE: use NULL as arguments substitution for %b and %c. This will be
+ *   replaced as corresponding empty LOB value placeholder, based on the
+ *   database specific representation.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $table
+ *   The name of the temporary table to select into. This name will not be
+ *   prefixed as there is no risk of collision.
+ * @return
+ *   A database query result resource, or FALSE if the query was not executed
+ *   correctly.
+ */
+function db_query_temporary($query) {
+  $args = func_get_args();
+  $tablename = array_pop($args);
+  array_shift($args);
+
+  $query = preg_replace('/^SELECT/i', 'CREATE GLOBAL TEMPORARY TABLE '. $tablename .' ON COMMIT PRESERVE ROWS AS SELECT', $query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  return db_query($query, $args);
+}
+
+/**
+ * Helper function for db_query_insert() and db_query_update().
+ */
+function _db_query_returning($query, $data, $lobs = array()) {
+  global $active_db, $last_result, $queries;
+
+  if (variable_get('dev_query', 0)) {
+    list($usec, $sec) = explode(' ', microtime());
+    $timer = (float)$usec + (float)$sec;
+  }
+
+  if (count($lobs)) {
+    foreach ($lobs as $key => $lob) {
+      $lob_fields[] = $lobs[$key]['field'];
+      $lob_locators[] = $lobs[$key]['locator'];
+    }
+    $query .= " RETURNING ". implode(', ', $lob_fields) ." INTO ". implode(', ', $lob_locators);
+  }
+
+  // Placeholders handling.
+  $query = db_prefix_tables($query);
+  $query = preg_replace_callback(ANSI_RESERVED_REGEXP, '_db_query_callback_ansi_reserved', $query);
+  $query = preg_replace_callback(ORACLE_RESERVED_REGEXP, '_db_query_callback_oci8_reserved', $query);
+  $query = preg_replace_callback(ORACLE_MAX30_REGEXP, '_db_query_callback_oci8_max30', $query);
+  _db_query_callback($data, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+
+  $last_result = oci_parse($active_db, $query);
+  if (count($lobs)) {
+    foreach ($lobs as $key => $lob) {
+      // Creates an "empty" OCI-Lob object to bind to the locator
+      $lobs[$key]['obj'] = oci_new_descriptor($active_db, OCI_D_LOB);
+      // Bind the returned Oracle LOB locator to the PHP LOB object
+      oci_bind_by_name($last_result, $lobs[$key]['locator'], $lobs[$key]['obj'], -1, $lobs[$key]['type']);
+    }
+  }
+
+  // Execute the statement using OCI_DEFAULT (begin a transaction)
+  if (!oci_execute($last_result, OCI_DEFAULT)) {
+    $error = oci_error($last_result);
+    trigger_error(check_plain($error['message']."\nquery: ". $error['sqltext']), E_USER_WARNING);
+    oci_rollback($active_db);
+    return FALSE;
+  }
+
+  // Now save a value to the LOB
+  if (count($lobs)) {
+    foreach ($lobs as $key => $lob) {
+      $obj = $lobs[$key]['obj'];
+      if (!(is_object($obj) && @$obj->truncate() && @$obj->save($lobs[$key]['data']))) {
+        oci_rollback($active_db);
+        return FALSE;
+      }
+    }
+  }
+
+  // On success, commit the transaction
+  oci_commit($active_db);
+
+  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);
+  }
+
+  // Free resources
+  if (count($lobs)) {
+    foreach ($lobs as $key => $lob) {
+      $lobs[$key]['obj']->free();
+    }
+  }
+
+  return TRUE;
+}
+
+/**
+ * Insert a row of record into database.
+ *
+ * @param $table
+ *   Table to insert.
+ * @param $values
+ *   An array containing the insert values. Each element of the array
+ *   should be an associatie array with the following keys:
+ *   - "field": The database field represented in the table column.
+ *   - "placeholder": The placeholder of the table column, using printf()
+ *     syntax. Valid %-modifiers are: %d, %f, %s and %b.
+ *   - "data": The data to insert into the table column.
+ * @return
+ *   A database query result resource, or FALSE if the query was not
+ *   executed correctly.
+ */
+function db_query_insert($table, $values) {
+  global $active_db, $last_result, $queries;
+
+  $fields = array();
+  $placeholders = array();
+  $data = array();
+  $lobs = array();
+  foreach ($values as $value) {
+    $fields[] = $value['field'];
+    switch ($value['placeholder']) {
+      case '%b':
+        $placeholders[] = 'EMPTY_BLOB()';
+        $lobs[] = array(
+          'field' => $value['field'],
+          'locator' => ":l". $value['field'],
+          'type' => SQLT_BLOB,
+          'data' => $value['data'],
+          'obj' => new stdClass(),
+        );
+        break;
+      case '%c':
+        $placeholders[] = 'EMPTY_CLOB()';
+        $lobs[] = array(
+          'field' => $value['field'],
+          'locator' => ":l". $value['field'],
+          'type' => SQLT_CLOB,
+          'data' => $value['data'],
+          'obj' => new stdClass(),
+        );
+        break;
+      default:
+        $placeholders[] = $value['placeholder'];
+        $data[] = $value['data'];
+        break;
+    }
+  }
+
+  if (!count($fields)) {
+    return;
+  }
+
+  $query = "INSERT INTO {". $table ."} (". implode(', ', $fields) .") VALUES (". implode(', ', $placeholders) .")";
+  return _db_query_returning($query, $data, $lobs);
+}
+
+/**
+ * Update a row of record in database.
+ *
+ * @param $table
+ *   Table to update.
+ * @param $values
+ *   An array containing the update values. Each element of the array
+ *   should be an associatie array with the following keys:
+ *   - "field": The database field represented in the table column.
+ *   - "placeholder": The placeholder of the table column, using printf()
+ *     syntax. Valid %-modifiers are: %d, %f, %s and %b.
+ *   - "data": The data to insert into the table column.
+ * @param $where_clause
+ *   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: %d, %f and %s.
+ *
+ *   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_update($table, $values, $where_clause = NULL) {
+  global $active_db, $last_result, $queries;
+
+  $args = func_get_args();
+  $args = array_slice($args, 3);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+
+  $fields = array();
+  $placeholders = array();
+  $data = array();
+  $lobs = array();
+  foreach ($values as $value) {
+    switch ($value['placeholder']) {
+      case '%b':
+        $fields[] = $value['field'] .' = EMPTY_BLOB()';
+        $lobs[] = array(
+          'field' => $value['field'],
+          'locator' => ":l". $value['field'],
+          'type' => SQLT_BLOB,
+          'data' => $value['data'],
+          'obj' => new stdClass(),
+        );
+        break;
+      case '%c':
+        $fields[] = $value['field'] .' = EMPTY_CLOB()';
+        $lobs[] = array(
+          'field' => $value['field'],
+          'locator' => ":l". $value['field'],
+          'type' => SQLT_CLOB,
+          'data' => $value['data'],
+          'obj' => new stdClass(),
+        );
+        break;
+      default:
+        $fields[] = $value['field'] .' = '. $value['placeholder'];
+        $data[] = $value['data'];
+        break;
+    }
+  }
+
+  if (!count($fields)) {
+    return;
+  }
+
+  $query = "UPDATE {". $table ."} SET ". implode(', ', $fields);
+  if ($where_clause) {
+    $query .= " WHERE ". $where_clause;
+    $data = array_merge($data, $args);
+  }
+  return _db_query_returning($query, $data, $lobs);
+}
+
+/**
+ * Delete a row of record from database.
+ *
+ * @param $table
+ *   Table to delete.
+ * @param $where_clause
+ *   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: %d, %f and %s.
+ *
+ *   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_delete($table, $where_clause = NULL) {
+  $args = func_get_args();
+  $args = array_slice($args, 2);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+
+  $data = array();
+
+  $query = "DELETE FROM {". $table ."}";
+  if ($where_clause) {
+    $query .= " WHERE ". $where_clause;
+    $data = $args;
+  }
+  return db_query($query, $data);
+}
+
+/**
+ * Prepare user input for use in a database query, preventing SQL injection attacks.
+ */
+function db_escape_string($text) {
+  // Trim first 4000 characters, based on Oracle VARCHAR2(4000) limitation.
+  $text = preg_replace('/(.{0,4000})(.*)/', '\1', $text);
+  // Replace any single ' with ''
+  $text = str_replace("'", "''", $text);
+  return $text;
+}
+
+/**
+ * Returns a properly formatted Binary Large OBject value.
+ * In case of Oracle, No processing is needed.
+ *
+ * @param $data
+ *   Data to encode.
+ * @return
+ *  Encoded data.
+ */
+function db_encode_blob($data) {
+  // NOTE: No processing is needed here, since LOBs in Oracle are input via
+  // LOB->save() and don't need to escape any charecters like \ or '.
+  return $data;
+}
+
+/**
+ * Returns text from a Binary Large OBject value.
+ * In case of Oracle LOBs are read via LOB->load().
+ *
+ * @param $data
+ *   Data to decode.
+ * @return
+ *  Decoded data.
+ */
+function db_decode_blob($data) {
+  return is_object($data) ? $data->load() : $data;
+}
+
+/**
+ * Returns a properly formatted Character Large OBject value.
+ * In case of Oracle, No processing is needed.
+ *
+ * @param $data
+ *   Data to encode.
+ * @return
+ *  Encoded data.
+ */
+function db_encode_clob($data) {
+  // NOTE: No processing is needed here, since LOBs in Oracle are input via
+  // LOB->save() and don't need to escape any charecters like \ or '.
+  return $data;
+}
+
+/**
+ * Returns text from a Character Large OBject value.
+ * In case of Oracle LOBs are read via LOB->load().
+ *
+ * @param $data
+ *   Data to decode.
+ * @return
+ *  Decoded data.
+ */
+function db_decode_clob($data) {
+  return is_object($data) ? $data->load() : $data;
+}
+
+/**
+ * Lock a table.
+ * This function automatically starts a transaction.
+ */
+function db_lock_table($table) {
+  db_query('LOCK TABLE {'. db_escape_table($table) .'} IN EXCLUSIVE MODE');
+}
+
+/**
+ * Unlock all locked tables.
+ * This function automatically commits a transaction.
+ */
+function db_unlock_tables() {
+  global $active_db;
+  oci_commit($active_db);
+}
+
+/**
+ * Check if a table exists.
+ */
+function db_table_exists($table) {
+  return db_result(db_query("SELECT COUNT(table_name) FROM user_tables WHERE table_name LIKE UPPER('{" . db_escape_table($table) . "}')"));
+}
+
+/**
+ * Check if a column exists in the given table.
+ */
+function db_column_exists($table, $column) {
+  return db_result(db_query("SELECT COUNT(column_name) FROM user_tab_cols WHERE table_name LIKE UPPER('{". db_escape_table($table) ."}') AND column_name LIKE UPPER(TRIM('$column'))"));
+}
+
+/**
+ * Check if constraint exists in the given table.
+ */
+function db_constraint_exists($table, $fields) {
+  $table = "{" . strtoupper($table) . "}";
+  foreach ($fields as $key => $value) {
+    $fields[$key] = strtoupper($value);
+  }
+
+  $queries = array();
+  foreach ($fields as $key => $value) {
+    $queries[] = "SELECT constraint_name FROM user_cons_columns WHERE table_name = '" . $table . "' AND column_name = '" . $value . "'";
+  }
+
+  return db_result(db_query(implode(' INTERSECT ', $queries)));
+}
+
+/**
+ * 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 ('. $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'  => 'VARCHAR2',
+
+    'text:tiny'       => 'VARCHAR2(4000)',
+    'text:small'      => 'VARCHAR2(4000)',
+    'text:medium'     => 'VARCHAR2(4000)',
+    'text:big'        => 'VARCHAR2(4000)',
+    'text:normal'     => 'VARCHAR2(4000)',
+
+    'int:tiny'        => 'INTEGER',
+    'int:small'       => 'INTEGER',
+    'int:medium'      => 'INTEGER',
+    'int:big'         => 'INTEGER',
+    'int:normal'      => 'INTEGER',
+
+    'float:tiny'      => 'FLOAT',
+    'float:small'     => 'FLOAT',
+    'float:medium'    => 'FLOAT',
+    'float:big'       => 'DOUBLE PRECISION',
+    'float:normal'    => 'FLOAT',
+
+    'numeric:normal'  => 'NUMERIC',
+
+    'blob:big'        => 'BLOB',
+    'blob:normal'     => 'BLOB',
+
+    'clob:big'        => 'CLOB',
+    'clob:normal'     => 'CLOB',
+
+    'datetime:normal' => 'TIMESTAMP',
+
+    'serial:tiny'     => 'INTEGER',
+    'serial:small'    => 'INTEGER',
+    'serial:medium'   => 'INTEGER',
+    'serial:big'      => 'INTEGER',
+    'serial:normal'   => 'INTEGER',
+  );
+  return $map;
+}
+
+/**
+ * Generate SQL to create a new table from a Drupal schema definition.
+ *
+ * @param $name
+ *   The name of the table to create.
+ * @param $table
+ *   A Schema API table definition array.
+ * @return
+ *   An array of SQL statements to create the table.
+ */
+function db_create_table_sql($name, $table) {
+  $sql_fields = array();
+  foreach ($table['fields'] as $field_name => $field) {
+    $spec = _db_process_field($field);
+    $sql_fields[] = _db_create_field_sql($field_name, $spec);
+    if ($spec['type'] == 'serial') {
+      $table['serials'][] = $field_name;
+    }
+  }
+
+  $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);
+    }
+  }
+
+  if (isset($table['serials']) && is_array($table['serials'])) {
+    foreach ($table['serials'] as $key_name => $key) {
+      $statements[] = _db_create_sequence_sql($name, $key);
+      $statements[] = _db_create_trigger_sql($name, $key);
+    }
+  }
+
+  return $statements;
+}
+
+function _db_create_sequence_sql($table, $field) {
+  $name = 'seq_{'. $table .'}_'. $field;
+  $query  = "CREATE SEQUENCE ". $name . " MINVALUE 1 INCREMENT BY 1 START WITH 1 NOCACHE NOORDER NOCYCLE";
+  return $query;
+}
+
+function _db_create_trigger_sql($table, $field) {
+  $query = "CREATE OR REPLACE TRIGGER tgr_{" . $table . "}_" . $field . "\n";
+  $query .= "BEFORE INSERT ON {" . $table . "}\n";
+  $query .= "FOR EACH ROW\n";
+  $query .= "BEGIN\n";
+  $query .= "\tIF :NEW." . $field . " IS NULL THEN\n";
+  $query .= "\t\tSELECT seq_{" . $table . "}_" . $field . ".NEXTVAL\n";
+  $query .= "\t\tINTO :NEW." . $field . "\n";
+  $query .= "\t\tFROM DUAL;\n";
+  $query .= "\tEND IF;\n";
+  $query .= "END;";
+  return $query;
+}
+
+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) {
+    if (is_array($field)) {
+      $ret[] = 'substr('. $field[0] .', 1, '. $field[1] .')';
+    }
+    else {
+      $ret[] = $field;
+    }
+  }
+  return implode(', ', $ret);
+}
+
+/**
+ * Set database-engine specific properties for a field.
+ *
+ * @param $field
+ *   A field description array, as specified in the schema documentation.
+ */
+function _db_process_field($field) {
+  if (!isset($field['size'])) {
+    $field['size'] = 'normal';
+  }
+  // Set the correct database-engine specific datatype.
+  if (!isset($field['oci8_type'])) {
+    $map = db_type_map();
+    $field['oci8_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.
+ * @param $table
+ *    Name of the table. Useful if required to create sequence and trigger.
+ * @param $extra_sql
+ *    Array of extra queries. Useful if required to create sequence and trigger.
+ */
+function _db_create_field_sql($name, $spec) {
+  $sql = $name .' '. $spec['oci8_type'];
+
+  if (($spec['type'] == 'text') || ($spec['type'] == 'varchar')) {
+    unset($spec['not null']);
+  }
+
+  if (!empty($spec['length'])) {
+    $sql .= '('. $spec['length'] .')';
+  }
+  elseif (isset($spec['precision']) && isset($spec['scale'])) {
+    $sql .= '('. $spec['scale'] .', '. $spec['precision'] .')';
+  }
+
+  if (isset($spec['default'])) {
+    $default = is_string($spec['default']) ? "'". $spec['default'] ."'" : $spec['default'];
+    $sql .= " DEFAULT $default";
+  }
+
+  if (isset($spec['not null']) && $spec['not null']) {
+    $sql .= ' NOT NULL';
+  }
+
+  if (!empty($spec['unsigned'])) {
+    $sql .= ' CHECK (' . $name . ' >= 0)';
+  }
+
+  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('ALTER TABLE {'. $table .'} RENAME TO {'. $new_name .'}');
+}
+
+/**
+ * Drop a table.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be dropped.
+ */
+function db_drop_table(&$ret, $table) {
+  $ret[] = update_sql('DROP TABLE {'. $table .'} CASCADE CONSTRAINTS');
+}
+
+/**
+ * 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) {
+  $spec = _db_process_field($field);
+  $queries[] = 'ALTER TABLE {'. $table .'} ADD ' . _db_create_field_sql($field, $spec);
+  if ($spec['type'] == 'serial') {
+    $queries[] = _db_create_sequence_sql($table, $field);
+    $queries[] = _db_create_trigger_sql($table, $field);
+  }
+  foreach ($queries as $query) {
+    $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 . '} MODIFY (' . $field . ' 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 . '} MODIFY (' . $field . ' DEFAULT NULL)');
+}
+
+/**
+ * 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.
+ *
+ * NOTE: Need to do some checking before add index, since
+ * Oracle don't allow create index on promary key.
+ */
+function db_add_index(&$ret, $table, $name, $fields) {
+  if (db_constraint_exists($table, $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("ALTER TABLE {". $table ."} RENAME $field TO 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/includes/install.inc drupal-6.x-dev-oci8/includes/install.inc
--- drupal-6.x-dev/includes/install.inc	2007-11-12 00:14:45.000000000 +0800
+++ drupal-6.x-dev-oci8/includes/install.inc	2007-11-17 00:44:22.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', 'oci8') 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/includes/install.oci8.inc drupal-6.x-dev-oci8/includes/install.oci8.inc
--- drupal-6.x-dev/includes/install.oci8.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-oci8/includes/install.oci8.inc	2007-11-17 00:41:51.000000000 +0800
@@ -0,0 +1,144 @@
+<?php
+// $Id$
+
+// Oracle specific install functions
+
+/**
+ * Check if Oracle is available.
+ *
+ * @return
+ *  TRUE/FALSE
+ */
+function oci8_is_available() {
+  return function_exists('oci_connect');
+}
+
+/**
+ * Check if we can connect to Oracle.
+ *
+ * @return
+ *  TRUE/FALSE
+ */
+function drupal_test_oci8($url, &$success) {
+  if (!oci8_is_available()) {
+    drupal_set_message(st('PHP OCI8 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 oci8 connection string and allow for non-standard Oracle port.
+  $conn_string = '//' . $url['host'] . (isset($url['port']) ? ':' . $url['port'] : '') . '/' . substr($url['path'], 1);
+
+  // Even we can indicate charset parameter for oci_connect, which will be
+  // used in the new connection, we will not use this feature. Therefore
+  // NLS_LANG environment variable will be used instead.
+  $connection = @oci_connect($url['user'], $url['pass'], $conn_string);
+
+  // Test connecting to the database.
+  if (!$connection && ($error = oci_error())) {
+    drupal_set_message(st('Failure to connect to your Oracle database server. Oracle 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.<br />Error: ' . $error, 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)';
+  $stmt = @oci_parse($connection, $query);
+  if (!$stmt && ($error = oci_error($connection))) {
+    drupal_set_message(st('We were unable to create a test table on your Oracle database server with the command %query. Oracle reports the following message: %error.<ul><li>Are you sure the configured username has the necessary Oracle permissions to create tables in the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%query' => $query, '%error' => $error)), 'error');
+    return FALSE;
+  }
+  $result = @oci_execute($stmt);
+  $err = FALSE;
+  $success[] = 'SELECT';
+  $success[] = 'CREATE';
+
+  // Test INSERT.
+  $query = 'INSERT INTO drupal_install_test (id) VALUES (1)';
+  $stmt = @oci_parse($connection, $query);
+  $result = @oci_execute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to insert a value into a test table on your Oracle database server. We tried inserting a value with the command %query and Oracle reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'INSERT';
+  }
+
+  // Test UPDATE.
+  $query = 'UPDATE drupal_install_test SET id = 2';
+  $stmt = @oci_parse($connection, $query);
+  $result = @oci_execute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to update a value in a test table on your Oracle database server. We tried updating a value with the command %query and Oracle reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'UPDATE';
+  }
+
+  // Test LOCK.
+  $query = 'LOCK TABLE drupal_install_test IN EXCLUSIVE MODE';
+  $stmt = @oci_parse($connection, $query);
+  $result = @oci_execute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to lock a test table on your Oracle database server. We tried locking a table with the command %query and Oracle 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 Oracle
+  $query = 'COMMIT';
+  $stmt = @oci_parse($connection, $query);
+  $result = @oci_execute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to unlock a test table on your Oracle database server. We tried unlocking a table with the command %query and Oracle reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'UNLOCK';
+  }
+
+  // Test DELETE.
+  $query = 'DELETE FROM drupal_install_test';
+  $stmt = @oci_parse($connection, $query);
+  $result = @oci_execute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to delete a value from a test table on your Oracle database server. We tried deleting a value with the command %query and Oracle reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'DELETE';
+  }
+
+  // Test DROP.
+  $query = 'DROP TABLE drupal_install_test';
+  $stmt = @oci_parse($connection, $query);
+  $result = @oci_execute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to drop a test table from your Oracle database server. We tried dropping a table with the command %query and Oracle reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'DROP';
+  }
+
+  if ($err) {
+    return FALSE;
+  }
+
+  oci_close($connection);
+  return TRUE;
+}
+
+?>
diff -urpN drupal-6.x-dev/INSTALL.oci8.txt drupal-6.x-dev-oci8/INSTALL.oci8.txt
--- drupal-6.x-dev/INSTALL.oci8.txt	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-oci8/INSTALL.oci8.txt	2007-11-17 00:41:37.000000000 +0800
@@ -0,0 +1,71 @@
+// $Id$
+
+CREATE THE Oracle DATABASE
+--------------------------
+
+This file describes how to create a Oracle database for Drupal.
+
+If you control your databases through a web-based control panel,
+check its documentation, as the following instructions are for the
+command line only.
+
+This step is only necessary if you don't already have a database
+set-up (e.g. by your host). In the following examples, 'dba_user' is
+an example Oracle user which has the CREATE and GRANT privileges. Use
+the appropriate user name for your system.
+
+Optionally, you can create a new tablespace for your Drupal site
+datebase. Log into sqlplus by following command:
+
+  sqlplus dba_user
+
+sqlplus will prompt for the 'dba_user' database password. At the
+sqlplus prompt, enter following command:
+
+  CREATE TABLESPACE tablespace_name
+  DATAFILE 'file_directory_path'
+  SIZE filesize AUTOEXTEND ON NEXT extend_size;
+
+where
+
+  'tablespace_name' is the name of you new tablespace
+  'file_directory_path' is the file location in database server
+  'filesize' is the initial file size, e.g. 50K, 1000M.
+  'extend_size' is the volumn for extension when data file is full
+
+First, you must create a new user for your Drupal site. At the
+sqlplus prompt, enter the following command:
+
+  CREATE USER username
+  IDENTIFIED BY "password";
+  DEFAULT TABLESPACE tablespace_name;
+  GRANT CONNECT, RESOURCE TO username;
+
+where
+
+  'tablespace_name' is the name of you tablespace
+  'username' is the username of your Oracle account
+  'password' is the password required for that username
+
+   Note: Unless your database user has all of the privileges listed
+   above, you will not be able to run Drupal.
+
+Oracle SPECIAL REQUIREMENTS
+---------------------------
+
+1. SYSTEM REQUIREMENT
+
+   Oracle driver for Drupal is fully tested with below softwares:
+     Oracle Database 10g Release 2 (10.2.0.1.0) for Linux x86,
+     Zend Core for Oracle v.2 Linux x86,
+     Zend Framework 1.0.0,
+     PHP Version 5.2.3,
+     Apache 2.2.3 (Debian)
+
+   You may use something newer than above, but there is no guarantee for
+   backward compatible.
+
+2. TABLE PREFIX LIMITATION
+
+   Table prefix are limited in maximum 12 characters. Proved by testing, it
+   is suggested to use table prefix within 10 characters.
diff -urpN drupal-6.x-dev/modules/system/system.module drupal-6.x-dev-oci8/modules/system/system.module
--- drupal-6.x-dev/modules/system/system.module	2007-11-11 16:48:22.000000000 +0800
+++ drupal-6.x-dev-oci8/modules/system/system.module	2007-11-17 00:44:48.000000000 +0800
@@ -12,6 +12,7 @@ define('DRUPAL_CORE_COMPATIBILITY', '6.x
 define('DRUPAL_MINIMUM_PHP',    '4.3.3');
 define('DRUPAL_MINIMUM_MYSQL',  '4.1.0'); // If using MySQL
 define('DRUPAL_MINIMUM_PGSQL',  '7.4');   // If using PostgreSQL
+define('DRUPAL_MINIMUM_OCI8',   '10.2');  // If using Oracle
 define('DRUPAL_MINIMUM_APACHE', '1.3');   // If using Apache
 
 // Maximum age of temporary files in seconds.
